libopenshot-audio-0.1.5/000077500000000000000000000000001320201440200151265ustar00rootroot00000000000000libopenshot-audio-0.1.5/.bzrignore000066400000000000000000000000521320201440200171250ustar00rootroot00000000000000build/ build/* .DS_Store .settings .idea/*libopenshot-audio-0.1.5/.cproject000066400000000000000000001021571320201440200167460ustar00rootroot00000000000000 cmake -G "Unix Makefiles" ../ -D"CMAKE_BUILD_TYPE:STRING=Release" true false true cmake -G "Unix Makefiles" ../ -D"CMAKE_BUILD_TYPE:STRING=Debug" true false true make test true false true make help true false true make doc true false true cmake -G "Unix Makefiles" ../ -D"CMAKE_BUILD_TYPE:STRING=Debug" true false true cmake -G "Unix Makefiles" ../ -D"CMAKE_BUILD_TYPE:STRING=Release" true false true cmake -G "MinGW Makefiles" ../ -D"CMAKE_BUILD_TYPE:STRING=Debug" true false true cmake -G "MinGW Makefiles" ../ -D"CMAKE_BUILD_TYPE:STRING=Release" true false true libopenshot-audio-0.1.5/.gitignore000066400000000000000000000000521320201440200171130ustar00rootroot00000000000000build/ build/* .DS_Store .settings .idea/*libopenshot-audio-0.1.5/.project000066400000000000000000000050101320201440200165710ustar00rootroot00000000000000 libopenshot-audio org.python.pydev.PyDevBuilder org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.buildArguments org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.buildLocation ${workspace_loc:/libopenshot-audio/build} org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget all org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd false org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.core.cnature org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature libopenshot-audio-0.1.5/AUTHORS000066400000000000000000000001301320201440200161700ustar00rootroot00000000000000Jonathan Thomas Julian Storer libopenshot-audio-0.1.5/CMakeLists.txt000066400000000000000000000167221320201440200176760ustar00rootroot00000000000000#################### CMakeLists.txt (libopenshot-audio) ###################### # @brief CMake build file for libopenshot-audio (used to generate makefiles) # @author Jonathan Thomas # # @section LICENSE # # Copyright (c) 2008-2016 OpenShot Studios, LLC # . This file is part of # OpenShot Audio Library (libopenshot-audio), an open-source project dedicated # to delivering high quality audio editing and playback solutions to the # world. For more information visit . # # OpenShot Audio Library (libopenshot-audio) is free software: you can # redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # OpenShot Audio Library (libopenshot-audio) is distributed in the hope that it # will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenShot Audio Library. If not, see . ################################################################################ cmake_minimum_required(VERSION 2.8) ################ ADD CMAKE MODULES ################## set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") ################ GET VERSION INFORMATION FROM VERSION.H ################## MESSAGE("--------------------------------------------------------------") MESSAGE("Determining Version Number (from Version.h file)") #### Get the lines related to libopenshot version from the Version.h header file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/include/Version.h OPENSHOT_AUDIO_VERSION_LINES REGEX "#define[ ]+OPENSHOT_AUDIO_VERSION_.*[0-9]+;.*") #### Set each line into it's own variable list (GET OPENSHOT_AUDIO_VERSION_LINES 0 LINE_MAJOR) list (GET OPENSHOT_AUDIO_VERSION_LINES 1 LINE_MINOR) list (GET OPENSHOT_AUDIO_VERSION_LINES 2 LINE_BUILD) list (GET OPENSHOT_AUDIO_VERSION_LINES 3 LINE_SO) #### Get the version number out of each line STRING(REGEX REPLACE "#define[ ]+OPENSHOT_AUDIO_VERSION_MAJOR.*([0-9])+;(.*)" "\\1" MAJOR_VERSION "${LINE_MAJOR}") STRING(REGEX REPLACE "#define[ ]+OPENSHOT_AUDIO_VERSION_MINOR.*([0-9])+;(.*)" "\\1" MINOR_VERSION "${LINE_MINOR}") STRING(REGEX REPLACE "#define[ ]+OPENSHOT_AUDIO_VERSION_BUILD.*([0-9])+;(.*)" "\\1" BUILD_VERSION "${LINE_BUILD}") STRING(REGEX REPLACE "#define[ ]+OPENSHOT_AUDIO_VERSION_SO.*([0-9])+;(.*)" "\\1" SO_VERSION "${LINE_SO}") set(PROJECT_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${BUILD_VERSION}") MESSAGE("--> MAJOR Version: ${MAJOR_VERSION}") MESSAGE("--> MINOR Version: ${MINOR_VERSION}") MESSAGE("--> BUILD Version: ${BUILD_VERSION}") MESSAGE("--> SO/API/ABI Version: ${SO_VERSION}") MESSAGE("--> VERSION: ${PROJECT_VERSION}") MESSAGE("") ################### SETUP PROJECT ################### project(openshot-audio) MESSAGE("--------------------------------------------------------------") MESSAGE("---- Generating build files for ${PROJECT_NAME} (${PROJECT_VERSION})") MESSAGE("--------------------------------------------------------------") # Enable stack-unwinding support in c objects on gcc-based platforms. # Failing to do so will cause your program to be terminated when a png # or a jpeg exception is thrown on linux or macosx. IF (CMAKE_COMPILER_IS_GNUCC) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexceptions") ENDIF(CMAKE_COMPILER_IS_GNUCC) IF (WIN32) SET_PROPERTY(GLOBAL PROPERTY JUCE_WINDOWS "JUCE_WINDOWS") SET_PROPERTY(GLOBAL PROPERTY JUCE_MINGW "JUCE_MINGW") SET(EXTENSION "cpp") # Find the base directory of the AISO SDK (if any) find_path(AISO_SDK_DIR iasiodrv.h PATHS $ENV{AISO_SDK_DIR} ) IF (AISO_SDK_DIR) MESSAGE("FOUND AISO_SDK_DIR: ${AISO_SDK_DIR}") ADD_DEFINITIONS(-DJUCE_ASIO=1) INCLUDE_DIRECTORIES(${AISO_SDK_DIR}) ELSE(AISO_SDK_DIR) MESSAGE("AISO_SDK_DIR NOT FOUND") ADD_DEFINITIONS(-DJUCE_ASIO=0) ENDIF (AISO_SDK_DIR) ADD_DEFINITIONS(-DDONT_AUTOLINK_TO_JUCE_LIBRARY) SET(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -std=c++0x") SET(JUCE_PLATFORM_SPECIFIC_LIBRARIES advapi32.lib comdlg32.lib gdi32.lib GlU32.lib Imm32.dll kernel32.lib ole32.lib OpenGL32.lib rpcrt4.lib shell32.lib Shlwapi.dll user32.lib vfw32.lib version.lib winmm.lib wininet.lib ws2_32.lib ) ELSE (WIN32) IF (UNIX) IF (APPLE) SET_PROPERTY(GLOBAL PROPERTY JUCE_MAC "JUCE_MAC") ADD_DEFINITIONS(-DNDEBUG) SET(EXTENSION "mm") SET(JUCE_PLATFORM_SPECIFIC_DIR build/macosx/platform_specific_code) SET(JUCE_PLATFORM_SPECIFIC_LIBRARIES "-framework Carbon -framework Cocoa -framework CoreFoundation -framework CoreAudio -framework CoreMidi -framework IOKit -framework AGL -framework AudioToolbox -framework QuartzCore -lobjc -framework Accelerate") SET(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -flax-vector-conversions -std=c++0x") ELSE (APPLE) SET_PROPERTY(GLOBAL PROPERTY JUCE_LINUX "JUCE_LINUX") SET(EXTENSION "cpp") FIND_PACKAGE(X11 REQUIRED) INCLUDE_DIRECTORIES(${X11_INCLUDE_DIR}) INCLUDE_DIRECTORIES(/usr/include/freetype2) ADD_DEFINITIONS(-DLINUX) SET(JUCE_PLATFORM_SPECIFIC_LIBRARIES ${X11_LIBRARIES} asound freetype Xinerama) ENDIF(APPLE) ENDIF(UNIX) ENDIF(WIN32) # Include header directories include_directories("JuceLibraryCode") # List of modules to build (Extension based on OS) FILE(GLOB JUCE_SOURCES JuceLibraryCode/modules/juce_core/juce_core.${EXTENSION} JuceLibraryCode/modules/juce_data_structures/juce_data_structures.${EXTENSION} JuceLibraryCode/modules/juce_events/juce_events.${EXTENSION} JuceLibraryCode/modules/juce_graphics/juce_graphics.${EXTENSION} JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.${EXTENSION} JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.${EXTENSION} JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.${EXTENSION} JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.${EXTENSION} JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.${EXTENSION} JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.${EXTENSION} ) # Disable RPATH SET(CMAKE_MACOSX_RPATH 0) ADD_LIBRARY(openshot-audio SHARED ${JUCE_SOURCES} ) # Set SONAME and other library properties set_target_properties(openshot-audio PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${SO_VERSION} INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib") TARGET_LINK_LIBRARIES(openshot-audio ${JUCE_PLATFORM_SPECIFIC_LIBRARIES} ) # PROCESS SUB-DIRECTORIES add_subdirectory(src) # Determine correct lib folder set(LIB_INSTALL_DIR lib${LIB_SUFFIX}) # Install Files INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/JuceLibraryCode/JuceHeader.h DESTINATION include/libopenshot-audio) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/JuceLibraryCode DESTINATION include/libopenshot-audio FILES_MATCHING PATTERN "*.h") INSTALL(TARGETS openshot-audio DESTINATION ${LIB_INSTALL_DIR}) ################### DOCUMENTATION ################### # Find Doxygen (used for documentation) include(cmake/Modules/UseDoxygen.cmake) # Install Doxygen html documentation file(GLOB_RECURSE doc_files ${CMAKE_CURRENT_BINARY_DIR}/doc/html/*.*) INSTALL(FILES ${doc_files} DESTINATION share/doc/libopenshot-audio) # Install manpage INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/openshot-audio-test-sound.1 DESTINATION share/man/man1) libopenshot-audio-0.1.5/COPYING000066400000000000000000000773271320201440200162010ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONSlibopenshot-audio-0.1.5/Doxyfile.in000066400000000000000000001744171320201440200172570ustar00rootroot00000000000000# Doxyfile 1.5.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "@PROJECT_NAME@" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = "@PROJECT_VERSION@" # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = "@DOXYFILE_OUTPUT_DIR@" # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, # Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = NO # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/src" "@CMAKE_CURRENT_SOURCE_DIR@/JuceLibraryCode" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = "_darcs" # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = "*/.*" "*/.*/*" # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/tests" # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@" # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = "@DOXYFILE_HTML_DIR@" # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to FRAME, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. Other possible values # for this tag are: HIERARCHIES, which will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list; # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which # disables this behavior completely. For backwards compatibility with previous # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE # respectively. GENERATE_TREEVIEW = NONE # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = @DOXYFILE_GENERATE_LATEX@ # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = "@DOXYFILE_LATEX_DIR@" # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = "@LATEX_COMPILER@" # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = "@MAKEINDEX_COMPILER@" # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = @DOXYFILE_PDFLATEX@ # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = YES # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = NO # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = @DOXYFILE_DOT@ # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = "@DOXYGEN_DOT_PATH@" # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Options related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO libopenshot-audio-0.1.5/JuceLibraryCode/000077500000000000000000000000001320201440200201345ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/AppConfig.h000066400000000000000000000112571320201440200221610ustar00rootroot00000000000000/* IMPORTANT! This file is auto-generated each time you save your project - if you alter its contents, your changes may be overwritten! There's a section below where you can add your own custom code safely, and the Introjucer will preserve the contents of that block, but the best way to change any of these definitions is by using the Introjucer's project settings. Any commented-out settings will assume their default values. */ #ifndef __JUCE_APPCONFIG_IIUU5J__ #define __JUCE_APPCONFIG_IIUU5J__ //============================================================================== // [BEGIN_USER_CODE_SECTION] // (You can add your own code in this section, and the Introjucer will not overwrite it) // [END_USER_CODE_SECTION] //============================================================================== #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 #define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 #define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 #define JUCE_MODULE_AVAILABLE_juce_audio_processors 1 #define JUCE_MODULE_AVAILABLE_juce_core 1 #define JUCE_MODULE_AVAILABLE_juce_data_structures 1 #define JUCE_MODULE_AVAILABLE_juce_events 1 #define JUCE_MODULE_AVAILABLE_juce_graphics 1 #define JUCE_MODULE_AVAILABLE_juce_gui_basics 1 #define JUCE_MODULE_AVAILABLE_juce_gui_extra 1 //============================================================================== #ifndef JUCE_STANDALONE_APPLICATION #define JUCE_STANDALONE_APPLICATION 0 #endif //============================================================================== // juce_audio_devices flags: #ifndef JUCE_ASIO //#define JUCE_ASIO #endif #ifndef JUCE_WASAPI //#define JUCE_WASAPI #endif #ifndef JUCE_WASAPI_EXCLUSIVE //#define JUCE_WASAPI_EXCLUSIVE #endif #ifndef JUCE_DIRECTSOUND //#define JUCE_DIRECTSOUND #endif #ifndef JUCE_ALSA //#define JUCE_ALSA #endif #ifndef JUCE_JACK //#define JUCE_JACK #endif #ifndef JUCE_USE_ANDROID_OPENSLES //#define JUCE_USE_ANDROID_OPENSLES #endif #ifndef JUCE_USE_CDREADER //#define JUCE_USE_CDREADER #endif #ifndef JUCE_USE_CDBURNER //#define JUCE_USE_CDBURNER #endif //============================================================================== // juce_audio_formats flags: #ifndef JUCE_USE_FLAC #define JUCE_USE_FLAC 0 #endif #ifndef JUCE_USE_OGGVORBIS #define JUCE_USE_OGGVORBIS 0 #endif #ifndef JUCE_USE_MP3AUDIOFORMAT #define JUCE_USE_MP3AUDIOFORMAT 0 #endif #ifndef JUCE_USE_LAME_AUDIO_FORMAT //#define JUCE_USE_LAME_AUDIO_FORMAT #endif #ifndef JUCE_USE_WINDOWS_MEDIA_FORMAT #define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 #endif //============================================================================== // juce_audio_processors flags: #ifndef JUCE_PLUGINHOST_VST //#define JUCE_PLUGINHOST_VST #endif #ifndef JUCE_PLUGINHOST_VST3 //#define JUCE_PLUGINHOST_VST3 #endif #ifndef JUCE_PLUGINHOST_AU //#define JUCE_PLUGINHOST_AU #endif //============================================================================== // juce_core flags: #ifndef JUCE_FORCE_DEBUG //#define JUCE_FORCE_DEBUG #endif #ifndef JUCE_LOG_ASSERTIONS //#define JUCE_LOG_ASSERTIONS #endif #ifndef JUCE_CHECK_MEMORY_LEAKS //#define JUCE_CHECK_MEMORY_LEAKS #endif #ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #endif #ifndef JUCE_INCLUDE_ZLIB_CODE //#define JUCE_INCLUDE_ZLIB_CODE #endif #ifndef JUCE_USE_CURL //#define JUCE_USE_CURL #endif //============================================================================== // juce_graphics flags: #ifndef JUCE_USE_COREIMAGE_LOADER #define JUCE_USE_COREIMAGE_LOADER 0 #endif #ifndef JUCE_USE_DIRECTWRITE #define JUCE_USE_DIRECTWRITE 0 #endif //============================================================================== // juce_gui_basics flags: #ifndef JUCE_ENABLE_REPAINT_DEBUGGING //#define JUCE_ENABLE_REPAINT_DEBUGGING #endif #ifndef JUCE_USE_XSHM //#define JUCE_USE_XSHM #endif #ifndef JUCE_USE_XRENDER //#define JUCE_USE_XRENDER #endif #ifndef JUCE_USE_XCURSOR //#define JUCE_USE_XCURSOR #endif //============================================================================== // juce_gui_extra flags: #ifndef JUCE_WEB_BROWSER #define JUCE_WEB_BROWSER 0 #endif #ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR #endif #endif // __JUCE_APPCONFIG_IIUU5J__ libopenshot-audio-0.1.5/JuceLibraryCode/JuceHeader.h000066400000000000000000000030701320201440200223040ustar00rootroot00000000000000/* IMPORTANT! This file is auto-generated each time you save your project - if you alter its contents, your changes may be overwritten! This is the header file that your files should include in order to get all the JUCE library headers. You should avoid including the JUCE headers directly in your own source files, because that wouldn't pick up the correct configuration options for your app. */ #ifndef __APPHEADERFILE_IIUU5J__ #define __APPHEADERFILE_IIUU5J__ #include "AppConfig.h" #include "modules/juce_audio_basics/juce_audio_basics.h" #include "modules/juce_audio_devices/juce_audio_devices.h" #include "modules/juce_audio_formats/juce_audio_formats.h" #include "modules/juce_audio_processors/juce_audio_processors.h" #include "modules/juce_core/juce_core.h" #include "modules/juce_data_structures/juce_data_structures.h" #include "modules/juce_events/juce_events.h" #include "modules/juce_graphics/juce_graphics.h" #include "modules/juce_gui_basics/juce_gui_basics.h" #include "modules/juce_gui_extra/juce_gui_extra.h" #if ! DONT_SET_USING_JUCE_NAMESPACE // If your code uses a lot of JUCE classes, then this will obviously save you // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE. using namespace juce; #endif #if ! JUCE_DONT_DECLARE_PROJECTINFO namespace ProjectInfo { const char* const projectName = "libopenshot-audio"; const char* const versionString = "0.0.1"; const int versionNumber = 0x1; } #endif #endif // __APPHEADERFILE_IIUU5J__ libopenshot-audio-0.1.5/JuceLibraryCode/ReadMe.txt000066400000000000000000000011231320201440200220270ustar00rootroot00000000000000 Important Note!! ================ The purpose of this folder is to contain files that are auto-generated by the Introjucer, and ALL files in this folder will be mercilessly DELETED and completely re-written whenever the Introjucer saves your project. Therefore, it's a bad idea to make any manual changes to the files in here, or to put any of your own files in here if you don't want to lose them. (Of course you may choose to add the folder's contents to your version-control system so that you can re-merge your own modifications after the Introjucer has saved its changes). libopenshot-audio-0.1.5/JuceLibraryCode/modules/000077500000000000000000000000001320201440200216045ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/000077500000000000000000000000001320201440200252375ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/buffers/000077500000000000000000000000001320201440200266735ustar00rootroot00000000000000juce_AudioDataConverters.cpp000066400000000000000000000521521320201440200342410ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/buffers/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { const double maxVal = (double) 0x7fff; char* intData = static_cast (dest); if (dest != (void*) source || destBytesPerSample <= 4) { for (int i = 0; i < numSamples; ++i) { *(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } else { intData += destBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; *(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { const double maxVal = (double) 0x7fff; char* intData = static_cast (dest); if (dest != (void*) source || destBytesPerSample <= 4) { for (int i = 0; i < numSamples; ++i) { *(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } else { intData += destBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; *(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { const double maxVal = (double) 0x7fffff; char* intData = static_cast (dest); if (dest != (void*) source || destBytesPerSample <= 4) { for (int i = 0; i < numSamples; ++i) { ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); intData += destBytesPerSample; } } else { intData += destBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); } } } void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { const double maxVal = (double) 0x7fffff; char* intData = static_cast (dest); if (dest != (void*) source || destBytesPerSample <= 4) { for (int i = 0; i < numSamples; ++i) { ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); intData += destBytesPerSample; } } else { intData += destBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); } } } void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { const double maxVal = (double) 0x7fffffff; char* intData = static_cast (dest); if (dest != (void*) source || destBytesPerSample <= 4) { for (int i = 0; i < numSamples; ++i) { *(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } else { intData += destBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; *(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { const double maxVal = (double) 0x7fffffff; char* intData = static_cast (dest); if (dest != (void*) source || destBytesPerSample <= 4) { for (int i = 0; i < numSamples; ++i) { *(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } else { intData += destBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; *(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data! char* d = static_cast (dest); for (int i = 0; i < numSamples; ++i) { *(float*) d = source[i]; #if JUCE_BIG_ENDIAN *(uint32*) d = ByteOrder::swap (*(uint32*) d); #endif d += destBytesPerSample; } } void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) { jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data! char* d = static_cast (dest); for (int i = 0; i < numSamples; ++i) { *(float*) d = source[i]; #if JUCE_LITTLE_ENDIAN *(uint32*) d = ByteOrder::swap (*(uint32*) d); #endif d += destBytesPerSample; } } //============================================================================== void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const float scale = 1.0f / 0x7fff; const char* intData = static_cast (source); if (source != (void*) dest || srcBytesPerSample >= 4) { for (int i = 0; i < numSamples; ++i) { dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData); intData += srcBytesPerSample; } } else { intData += srcBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData); } } } void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const float scale = 1.0f / 0x7fff; const char* intData = static_cast (source); if (source != (void*) dest || srcBytesPerSample >= 4) { for (int i = 0; i < numSamples; ++i) { dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData); intData += srcBytesPerSample; } } else { intData += srcBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData); } } } void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const float scale = 1.0f / 0x7fffff; const char* intData = static_cast (source); if (source != (void*) dest || srcBytesPerSample >= 4) { for (int i = 0; i < numSamples; ++i) { dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData); intData += srcBytesPerSample; } } else { intData += srcBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData); } } } void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const float scale = 1.0f / 0x7fffff; const char* intData = static_cast (source); if (source != (void*) dest || srcBytesPerSample >= 4) { for (int i = 0; i < numSamples; ++i) { dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData); intData += srcBytesPerSample; } } else { intData += srcBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData); } } } void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const float scale = 1.0f / 0x7fffffff; const char* intData = static_cast (source); if (source != (void*) dest || srcBytesPerSample >= 4) { for (int i = 0; i < numSamples; ++i) { dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData); intData += srcBytesPerSample; } } else { intData += srcBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData); } } } void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const float scale = 1.0f / 0x7fffffff; const char* intData = static_cast (source); if (source != (void*) dest || srcBytesPerSample >= 4) { for (int i = 0; i < numSamples; ++i) { dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData); intData += srcBytesPerSample; } } else { intData += srcBytesPerSample * numSamples; for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData); } } } void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const char* s = static_cast (source); for (int i = 0; i < numSamples; ++i) { dest[i] = *(float*)s; #if JUCE_BIG_ENDIAN uint32* const d = (uint32*) (dest + i); *d = ByteOrder::swap (*d); #endif s += srcBytesPerSample; } } void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) { const char* s = static_cast (source); for (int i = 0; i < numSamples; ++i) { dest[i] = *(float*)s; #if JUCE_LITTLE_ENDIAN uint32* const d = (uint32*) (dest + i); *d = ByteOrder::swap (*d); #endif s += srcBytesPerSample; } } //============================================================================== void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat, const float* const source, void* const dest, const int numSamples) { switch (destFormat) { case int16LE: convertFloatToInt16LE (source, dest, numSamples); break; case int16BE: convertFloatToInt16BE (source, dest, numSamples); break; case int24LE: convertFloatToInt24LE (source, dest, numSamples); break; case int24BE: convertFloatToInt24BE (source, dest, numSamples); break; case int32LE: convertFloatToInt32LE (source, dest, numSamples); break; case int32BE: convertFloatToInt32BE (source, dest, numSamples); break; case float32LE: convertFloatToFloat32LE (source, dest, numSamples); break; case float32BE: convertFloatToFloat32BE (source, dest, numSamples); break; default: jassertfalse; break; } } void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat, const void* const source, float* const dest, const int numSamples) { switch (sourceFormat) { case int16LE: convertInt16LEToFloat (source, dest, numSamples); break; case int16BE: convertInt16BEToFloat (source, dest, numSamples); break; case int24LE: convertInt24LEToFloat (source, dest, numSamples); break; case int24BE: convertInt24BEToFloat (source, dest, numSamples); break; case int32LE: convertInt32LEToFloat (source, dest, numSamples); break; case int32BE: convertInt32BEToFloat (source, dest, numSamples); break; case float32LE: convertFloat32LEToFloat (source, dest, numSamples); break; case float32BE: convertFloat32BEToFloat (source, dest, numSamples); break; default: jassertfalse; break; } } //============================================================================== void AudioDataConverters::interleaveSamples (const float** const source, float* const dest, const int numSamples, const int numChannels) { for (int chan = 0; chan < numChannels; ++chan) { int i = chan; const float* src = source [chan]; for (int j = 0; j < numSamples; ++j) { dest [i] = src [j]; i += numChannels; } } } void AudioDataConverters::deinterleaveSamples (const float* const source, float** const dest, const int numSamples, const int numChannels) { for (int chan = 0; chan < numChannels; ++chan) { int i = chan; float* dst = dest [chan]; for (int j = 0; j < numSamples; ++j) { dst [j] = source [i]; i += numChannels; } } } //============================================================================== #if JUCE_UNIT_TESTS class AudioConversionTests : public UnitTest { public: AudioConversionTests() : UnitTest ("Audio data conversion") {} template struct Test5 { static void test (UnitTest& unitTest, Random& r) { test (unitTest, false, r); test (unitTest, true, r); } static void test (UnitTest& unitTest, bool inPlace, Random& r) { const int numSamples = 2048; int32 original [numSamples], converted [numSamples], reversed [numSamples]; { AudioData::Pointer d (original); bool clippingFailed = false; for (int i = 0; i < numSamples / 2; ++i) { d.setAsFloat (r.nextFloat() * 2.2f - 1.1f); if (! d.isFloatingPoint()) clippingFailed = d.getAsFloat() > 1.0f || d.getAsFloat() < -1.0f || clippingFailed; ++d; d.setAsInt32 (r.nextInt()); ++d; } unitTest.expect (! clippingFailed); } // convert data from the source to dest format.. ScopedPointer conv (new AudioData::ConverterInstance , AudioData::Pointer >()); conv->convertSamples (inPlace ? reversed : converted, original, numSamples); // ..and back again.. conv = new AudioData::ConverterInstance , AudioData::Pointer >(); if (! inPlace) zeromem (reversed, sizeof (reversed)); conv->convertSamples (reversed, inPlace ? reversed : converted, numSamples); { int biggestDiff = 0; AudioData::Pointer d1 (original); AudioData::Pointer d2 (reversed); const int errorMargin = 2 * AudioData::Pointer::get32BitResolution() + AudioData::Pointer::get32BitResolution(); for (int i = 0; i < numSamples; ++i) { biggestDiff = jmax (biggestDiff, std::abs (d1.getAsInt32() - d2.getAsInt32())); ++d1; ++d2; } unitTest.expect (biggestDiff <= errorMargin); } } }; template struct Test3 { static void test (UnitTest& unitTest, Random& r) { Test5 ::test (unitTest, r); Test5 ::test (unitTest, r); } }; template struct Test2 { static void test (UnitTest& unitTest, Random& r) { Test3 ::test (unitTest, r); Test3 ::test (unitTest, r); Test3 ::test (unitTest, r); Test3 ::test (unitTest, r); Test3 ::test (unitTest, r); Test3 ::test (unitTest, r); } }; template struct Test1 { static void test (UnitTest& unitTest, Random& r) { Test2 ::test (unitTest, r); Test2 ::test (unitTest, r); } }; void runTest() { Random r = getRandom(); beginTest ("Round-trip conversion: Int8"); Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int16"); Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int24"); Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int32"); Test1 ::test (*this, r); beginTest ("Round-trip conversion: Float32"); Test1 ::test (*this, r); } }; static AudioConversionTests audioConversionUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h000066400000000000000000001213141320201440200337620ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIODATACONVERTERS_H_INCLUDED #define JUCE_AUDIODATACONVERTERS_H_INCLUDED //============================================================================== /** This class a container which holds all the classes pertaining to the AudioData::Pointer audio sample format class. @see AudioData::Pointer. */ class JUCE_API AudioData { public: //============================================================================== // These types can be used as the SampleFormat template parameter for the AudioData::Pointer class. class Int8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit integer packed data format. */ class UInt8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit unsigned integer packed data format. */ class Int16; /**< Used as a template parameter for AudioData::Pointer. Indicates an 16-bit integer packed data format. */ class Int24; /**< Used as a template parameter for AudioData::Pointer. Indicates an 24-bit integer packed data format. */ class Int32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit integer packed data format. */ class Float32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit float data format. */ //============================================================================== // These types can be used as the Endianness template parameter for the AudioData::Pointer class. class BigEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in big-endian order. */ class LittleEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in little-endian order. */ class NativeEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in the CPU's native endianness. */ //============================================================================== // These types can be used as the InterleavingType template parameter for the AudioData::Pointer class. class NonInterleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored contiguously. */ class Interleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are interleaved with a number of other channels. */ //============================================================================== // These types can be used as the Constness template parameter for the AudioData::Pointer class. class NonConst; /**< Used as a template parameter for AudioData::Pointer. Indicates that the pointer can be used for non-const data. */ class Const; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples can only be used for const data.. */ #ifndef DOXYGEN //============================================================================== class BigEndian { public: template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); } template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); } template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); } template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); } template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); } enum { isBigEndian = 1 }; }; class LittleEndian { public: template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); } template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); } template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); } template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); } template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); } enum { isBigEndian = 0 }; }; #if JUCE_BIG_ENDIAN class NativeEndian : public BigEndian {}; #else class NativeEndian : public LittleEndian {}; #endif //============================================================================== class Int8 { public: inline Int8 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } inline float getAsFloatLE() const noexcept { return (float) (*data * (1.0 / (1.0 + maxValue))); } inline float getAsFloatBE() const noexcept { return getAsFloatLE(); } inline void setAsFloatLE (float newValue) noexcept { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))); } inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); } inline int32 getAsInt32LE() const noexcept { return (int) (*data << 24); } inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); } inline void setAsInt32LE (int newValue) noexcept { *data = (int8) (newValue >> 24); } inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); } inline void clear() noexcept { *data = 0; } inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } inline void copyFromSameType (Int8& source) noexcept { *data = *source.data; } int8* data; enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 }; }; class UInt8 { public: inline UInt8 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } inline float getAsFloatLE() const noexcept { return (float) ((*data - 128) * (1.0 / (1.0 + maxValue))); } inline float getAsFloatBE() const noexcept { return getAsFloatLE(); } inline void setAsFloatLE (float newValue) noexcept { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + maxValue))); } inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); } inline int32 getAsInt32LE() const noexcept { return (int) ((*data - 128) << 24); } inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); } inline void setAsInt32LE (int newValue) noexcept { *data = (uint8) (128 + (newValue >> 24)); } inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); } inline void clear() noexcept { *data = 128; } inline void clearMultiple (int num) noexcept { memset (data, 128, (size_t) num) ;} template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } inline void copyFromSameType (UInt8& source) noexcept { *data = *source.data; } uint8* data; enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 }; }; class Int16 { public: inline Int16 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfBigEndian (*data)); } inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfLittleEndian (*data)); } inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); } inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); } inline int32 getAsInt32LE() const noexcept { return (int32) (ByteOrder::swapIfBigEndian ((uint16) *data) << 16); } inline int32 getAsInt32BE() const noexcept { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); } inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); } inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) (newValue >> 16)); } inline void clear() noexcept { *data = 0; } inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } inline void copyFromSameType (Int16& source) noexcept { *data = *source.data; } uint16* data; enum { bytesPerSample = 2, maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 }; }; class Int24 { public: inline Int24 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { data += 3; } inline void skip (int numSamples) noexcept { data += 3 * numSamples; } inline float getAsFloatLE() const noexcept { return (float) (ByteOrder::littleEndian24Bit (data) * (1.0 / (1.0 + maxValue))); } inline float getAsFloatBE() const noexcept { return (float) (ByteOrder::bigEndian24Bit (data) * (1.0 / (1.0 + maxValue))); } inline void setAsFloatLE (float newValue) noexcept { ByteOrder::littleEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); } inline void setAsFloatBE (float newValue) noexcept { ByteOrder::bigEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); } inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::littleEndian24Bit (data) << 8; } inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::bigEndian24Bit (data) << 8; } inline void setAsInt32LE (int32 newValue) noexcept { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); } inline void setAsInt32BE (int32 newValue) noexcept { ByteOrder::bigEndian24BitToChars (newValue >> 8, data); } inline void clear() noexcept { data[0] = 0; data[1] = 0; data[2] = 0; } inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } inline void copyFromSameType (Int24& source) noexcept { data[0] = source.data[0]; data[1] = source.data[1]; data[2] = source.data[2]; } char* data; enum { bytesPerSample = 3, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; }; class Int32 { public: inline Int32 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); } inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data); } inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); } inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); } inline void clear() noexcept { *data = 0; } inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } inline void copyFromSameType (Int32& source) noexcept { *data = *source.data; } uint32* data; enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 }; }; /** A 32-bit integer type, of which only the bottom 24 bits are used. */ class Int24in32 : public Int32 { public: inline Int24in32 (void* d) noexcept : Int32 (d) {} inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data) << 8; } inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data) << 8; } inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue >> 8); } inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue >> 8); } template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } inline void copyFromSameType (Int24in32& source) noexcept { *data = *source.data; } enum { bytesPerSample = 4, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; }; class Float32 { public: inline Float32 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } #if JUCE_BIG_ENDIAN inline float getAsFloatBE() const noexcept { return *data; } inline void setAsFloatBE (float newValue) noexcept { *data = newValue; } inline float getAsFloatLE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; } inline void setAsFloatLE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); } #else inline float getAsFloatLE() const noexcept { return *data; } inline void setAsFloatLE (float newValue) noexcept { *data = newValue; } inline float getAsFloatBE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; } inline void setAsFloatBE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); } #endif inline int32 getAsInt32LE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatLE()) * (double) maxValue); } inline int32 getAsInt32BE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatBE()) * (double) maxValue); } inline void setAsInt32LE (int32 newValue) noexcept { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } inline void setAsInt32BE (int32 newValue) noexcept { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } inline void clear() noexcept { *data = 0; } inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} template inline void copyFromLE (SourceType& source) noexcept { setAsFloatLE (source.getAsFloat()); } template inline void copyFromBE (SourceType& source) noexcept { setAsFloatBE (source.getAsFloat()); } inline void copyFromSameType (Float32& source) noexcept { *data = *source.data; } float* data; enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = (1 << 8), isFloat = 1 }; }; //============================================================================== class NonInterleaved { public: inline NonInterleaved() noexcept {} inline NonInterleaved (const NonInterleaved&) noexcept {} inline NonInterleaved (const int) noexcept {} inline void copyFrom (const NonInterleaved&) noexcept {} template inline void advanceData (SampleFormatType& s) noexcept { s.advance(); } template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numSamples); } template inline void clear (SampleFormatType& s, int numSamples) noexcept { s.clearMultiple (numSamples); } template inline static int getNumBytesBetweenSamples (const SampleFormatType&) noexcept { return SampleFormatType::bytesPerSample; } enum { isInterleavedType = 0, numInterleavedChannels = 1 }; }; class Interleaved { public: inline Interleaved() noexcept : numInterleavedChannels (1) {} inline Interleaved (const Interleaved& other) noexcept : numInterleavedChannels (other.numInterleavedChannels) {} inline Interleaved (const int numInterleavedChans) noexcept : numInterleavedChannels (numInterleavedChans) {} inline void copyFrom (const Interleaved& other) noexcept { numInterleavedChannels = other.numInterleavedChannels; } template inline void advanceData (SampleFormatType& s) noexcept { s.skip (numInterleavedChannels); } template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numInterleavedChannels * numSamples); } template inline void clear (SampleFormatType& s, int numSamples) noexcept { while (--numSamples >= 0) { s.clear(); s.skip (numInterleavedChannels); } } template inline int getNumBytesBetweenSamples (const SampleFormatType&) const noexcept { return numInterleavedChannels * SampleFormatType::bytesPerSample; } int numInterleavedChannels; enum { isInterleavedType = 1 }; }; //============================================================================== class NonConst { public: typedef void VoidType; static inline void* toVoidPtr (VoidType* v) noexcept { return v; } enum { isConst = 0 }; }; class Const { public: typedef const void VoidType; static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast (v); } enum { isConst = 1 }; }; #endif //============================================================================== /** A pointer to a block of audio data with a particular encoding. This object can be used to read and write from blocks of encoded audio samples. To create one, you specify the audio format as a series of template parameters, e.g. @code // this creates a pointer for reading from a const array of 16-bit little-endian packed samples. AudioData::Pointer pointer (someRawAudioData); // These methods read the sample that is being pointed to float firstSampleAsFloat = pointer.getAsFloat(); int32 firstSampleAsInt = pointer.getAsInt32(); ++pointer; // moves the pointer to the next sample. pointer += 3; // skips the next 3 samples. @endcode The convertSamples() method lets you copy a range of samples from one format to another, automatically converting its format. @see AudioData::Converter */ template class Pointer : private InterleavingType // (inherited for EBCO) { public: //============================================================================== /** Creates a non-interleaved pointer from some raw data in the appropriate format. This constructor is only used if you've specified the AudioData::NonInterleaved option - for interleaved formats, use the constructor that also takes a number of channels. */ Pointer (typename Constness::VoidType* sourceData) noexcept : data (Constness::toVoidPtr (sourceData)) { // If you're using interleaved data, call the other constructor! If you're using non-interleaved data, // you should pass NonInterleaved as the template parameter for the interleaving type! static_jassert (InterleavingType::isInterleavedType == 0); } /** Creates a pointer from some raw data in the appropriate format with the specified number of interleaved channels. For non-interleaved data, use the other constructor. */ Pointer (typename Constness::VoidType* sourceData, int numInterleaved) noexcept : InterleavingType (numInterleaved), data (Constness::toVoidPtr (sourceData)) { } /** Creates a copy of another pointer. */ Pointer (const Pointer& other) noexcept : InterleavingType (other), data (other.data) { } Pointer& operator= (const Pointer& other) noexcept { InterleavingType::operator= (other); data = other.data; return *this; } //============================================================================== /** Returns the value of the first sample as a floating point value. The value will be in the range -1.0 to 1.0 for integer formats. For floating point formats, the value could be outside that range, although -1 to 1 is the standard range. */ inline float getAsFloat() const noexcept { return Endianness::getAsFloat (data); } /** Sets the value of the first sample as a floating point value. (This method can only be used if the AudioData::NonConst option was used). The value should be in the range -1.0 to 1.0 - for integer formats, values outside that range will be clipped. For floating point formats, any value passed in here will be written directly, although -1 to 1 is the standard range. */ inline void setAsFloat (float newValue) noexcept { static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! Endianness::setAsFloat (data, newValue); } /** Returns the value of the first sample as a 32-bit integer. The value returned will be in the range 0x80000000 to 0x7fffffff, and shorter values will be shifted to fill this range (e.g. if you're reading from 24-bit data, the values will be shifted up by 8 bits when returned here). If the source data is floating point, values beyond -1.0 to 1.0 will be clipped so that -1.0 maps onto -0x7fffffff and 1.0 maps to 0x7fffffff. */ inline int32 getAsInt32() const noexcept { return Endianness::getAsInt32 (data); } /** Sets the value of the first sample as a 32-bit integer. This will be mapped to the range of the format that is being written - see getAsInt32(). */ inline void setAsInt32 (int32 newValue) noexcept { static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! Endianness::setAsInt32 (data, newValue); } /** Moves the pointer along to the next sample. */ inline Pointer& operator++() noexcept { advance(); return *this; } /** Moves the pointer back to the previous sample. */ inline Pointer& operator--() noexcept { this->advanceDataBy (data, -1); return *this; } /** Adds a number of samples to the pointer's position. */ Pointer& operator+= (int samplesToJump) noexcept { this->advanceDataBy (data, samplesToJump); return *this; } /** Writes a stream of samples into this pointer from another pointer. This will copy the specified number of samples, converting between formats appropriately. */ void convertSamples (Pointer source, int numSamples) const noexcept { static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! for (Pointer dest (*this); --numSamples >= 0;) { dest.data.copyFromSameType (source.data); dest.advance(); source.advance(); } } /** Writes a stream of samples into this pointer from another pointer. This will copy the specified number of samples, converting between formats appropriately. */ template void convertSamples (OtherPointerType source, int numSamples) const noexcept { static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! Pointer dest (*this); if (source.getRawData() != getRawData() || source.getNumBytesBetweenSamples() >= getNumBytesBetweenSamples()) { while (--numSamples >= 0) { Endianness::copyFrom (dest.data, source); dest.advance(); ++source; } } else // copy backwards if we're increasing the sample width.. { dest += numSamples; source += numSamples; while (--numSamples >= 0) Endianness::copyFrom ((--dest).data, --source); } } /** Sets a number of samples to zero. */ void clearSamples (int numSamples) const noexcept { Pointer dest (*this); dest.clear (dest.data, numSamples); } /** Scans a block of data, returning the lowest and highest levels as floats */ Range findMinAndMax (size_t numSamples) const noexcept { if (numSamples == 0) return Range(); Pointer dest (*this); if (isFloatingPoint()) { float mn = dest.getAsFloat(); dest.advance(); float mx = mn; while (--numSamples > 0) { const float v = dest.getAsFloat(); dest.advance(); if (mx < v) mx = v; if (v < mn) mn = v; } return Range (mn, mx); } int32 mn = dest.getAsInt32(); dest.advance(); int32 mx = mn; while (--numSamples > 0) { const int v = dest.getAsInt32(); dest.advance(); if (mx < v) mx = v; if (v < mn) mn = v; } return Range (mn * (float) (1.0 / (1.0 + Int32::maxValue)), mx * (float) (1.0 / (1.0 + Int32::maxValue))); } /** Scans a block of data, returning the lowest and highest levels as floats */ void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept { Range r (findMinAndMax (numSamples)); minValue = r.getStart(); maxValue = r.getEnd(); } /** Returns true if the pointer is using a floating-point format. */ static bool isFloatingPoint() noexcept { return (bool) SampleFormat::isFloat; } /** Returns true if the format is big-endian. */ static bool isBigEndian() noexcept { return (bool) Endianness::isBigEndian; } /** Returns the number of bytes in each sample (ignoring the number of interleaved channels). */ static int getBytesPerSample() noexcept { return (int) SampleFormat::bytesPerSample; } /** Returns the number of interleaved channels in the format. */ int getNumInterleavedChannels() const noexcept { return (int) this->numInterleavedChannels; } /** Returns the number of bytes between the start address of each sample. */ int getNumBytesBetweenSamples() const noexcept { return InterleavingType::getNumBytesBetweenSamples (data); } /** Returns the accuracy of this format when represented as a 32-bit integer. This is the smallest number above 0 that can be represented in the sample format, converted to a 32-bit range. E,g. if the format is 8-bit, its resolution is 0x01000000; if the format is 24-bit, its resolution is 0x100. */ static int get32BitResolution() noexcept { return (int) SampleFormat::resolution; } /** Returns a pointer to the underlying data. */ const void* getRawData() const noexcept { return data.data; } private: //============================================================================== SampleFormat data; inline void advance() noexcept { this->advanceData (data); } Pointer operator++ (int); // private to force you to use the more efficient pre-increment! Pointer operator-- (int); }; //============================================================================== /** A base class for objects that are used to convert between two different sample formats. The AudioData::ConverterInstance implements this base class and can be templated, so you can create an instance that converts between two particular formats, and then store this in the abstract base class. @see AudioData::ConverterInstance */ class Converter { public: virtual ~Converter() {} /** Converts a sequence of samples from the converter's source format into the dest format. */ virtual void convertSamples (void* destSamples, const void* sourceSamples, int numSamples) const = 0; /** Converts a sequence of samples from the converter's source format into the dest format. This method takes sub-channel indexes, which can be used with interleaved formats in order to choose a particular sub-channel of the data to be used. */ virtual void convertSamples (void* destSamples, int destSubChannel, const void* sourceSamples, int sourceSubChannel, int numSamples) const = 0; }; //============================================================================== /** A class that converts between two templated AudioData::Pointer types, and which implements the AudioData::Converter interface. This can be used as a concrete instance of the AudioData::Converter abstract class. @see AudioData::Converter */ template class ConverterInstance : public Converter { public: ConverterInstance (int numSourceChannels = 1, int numDestChannels = 1) : sourceChannels (numSourceChannels), destChannels (numDestChannels) {} void convertSamples (void* dest, const void* source, int numSamples) const override { SourceSampleType s (source, sourceChannels); DestSampleType d (dest, destChannels); d.convertSamples (s, numSamples); } void convertSamples (void* dest, int destSubChannel, const void* source, int sourceSubChannel, int numSamples) const override { jassert (destSubChannel < destChannels && sourceSubChannel < sourceChannels); SourceSampleType s (addBytesToPointer (source, sourceSubChannel * SourceSampleType::getBytesPerSample()), sourceChannels); DestSampleType d (addBytesToPointer (dest, destSubChannel * DestSampleType::getBytesPerSample()), destChannels); d.convertSamples (s, numSamples); } private: JUCE_DECLARE_NON_COPYABLE (ConverterInstance) const int sourceChannels, destChannels; }; }; //============================================================================== /** A set of routines to convert buffers of 32-bit floating point data to and from various integer formats. Note that these functions are deprecated - the AudioData class provides a much more flexible set of conversion classes now. */ class JUCE_API AudioDataConverters { public: //============================================================================== static void convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2); static void convertFloatToInt16BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2); static void convertFloatToInt24LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3); static void convertFloatToInt24BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3); static void convertFloatToInt32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); static void convertFloatToInt32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); static void convertFloatToFloat32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); static void convertFloatToFloat32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); //============================================================================== static void convertInt16LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2); static void convertInt16BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2); static void convertInt24LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3); static void convertInt24BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3); static void convertInt32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); static void convertInt32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); static void convertFloat32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); static void convertFloat32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); //============================================================================== enum DataFormat { int16LE, int16BE, int24LE, int24BE, int32LE, int32BE, float32LE, float32BE, }; static void convertFloatToFormat (DataFormat destFormat, const float* source, void* dest, int numSamples); static void convertFormatToFloat (DataFormat sourceFormat, const void* source, float* dest, int numSamples); //============================================================================== static void interleaveSamples (const float** source, float* dest, int numSamples, int numChannels); static void deinterleaveSamples (const float* source, float** dest, int numSamples, int numChannels); private: AudioDataConverters(); JUCE_DECLARE_NON_COPYABLE (AudioDataConverters) }; #endif // JUCE_AUDIODATACONVERTERS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp000066400000000000000000000543041320201440200337500ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioSampleBuffer::AudioSampleBuffer() noexcept : numChannels (0), size (0), allocatedBytes (0), channels (static_cast (preallocatedChannelSpace)), isClear (false) { } AudioSampleBuffer::AudioSampleBuffer (const int numChans, const int numSamples) noexcept : numChannels (numChans), size (numSamples) { jassert (numSamples >= 0); jassert (numChans >= 0); allocateData(); } AudioSampleBuffer::AudioSampleBuffer (const AudioSampleBuffer& other) noexcept : numChannels (other.numChannels), size (other.size), allocatedBytes (other.allocatedBytes) { if (allocatedBytes == 0) { allocateChannels (other.channels, 0); } else { allocateData(); if (other.isClear) { clear(); } else { for (int i = 0; i < numChannels; ++i) FloatVectorOperations::copy (channels[i], other.channels[i], size); } } } void AudioSampleBuffer::allocateData() { const size_t channelListSize = sizeof (float*) * (size_t) (numChannels + 1); allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (float) + channelListSize + 32; allocatedData.malloc (allocatedBytes); channels = reinterpret_cast (allocatedData.getData()); float* chan = (float*) (allocatedData + channelListSize); for (int i = 0; i < numChannels; ++i) { channels[i] = chan; chan += size; } channels [numChannels] = nullptr; isClear = false; } AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo, const int numChans, const int numSamples) noexcept : numChannels (numChans), size (numSamples), allocatedBytes (0) { jassert (dataToReferTo != nullptr); jassert (numChans >= 0 && numSamples >= 0); allocateChannels (dataToReferTo, 0); } AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo, const int numChans, const int startSample, const int numSamples) noexcept : numChannels (numChans), size (numSamples), allocatedBytes (0), isClear (false) { jassert (dataToReferTo != nullptr); jassert (numChans >= 0 && startSample >= 0 && numSamples >= 0); allocateChannels (dataToReferTo, startSample); } void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, const int newNumChannels, const int newNumSamples) noexcept { jassert (dataToReferTo != nullptr); jassert (newNumChannels >= 0 && newNumSamples >= 0); if (allocatedBytes != 0) { allocatedBytes = 0; allocatedData.free(); } numChannels = newNumChannels; size = newNumSamples; allocateChannels (dataToReferTo, 0); jassert (! isClear); } void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset) { jassert (offset >= 0); // (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools) if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) { channels = static_cast (preallocatedChannelSpace); } else { allocatedData.malloc ((size_t) numChannels + 1, sizeof (float*)); channels = reinterpret_cast (allocatedData.getData()); } for (int i = 0; i < numChannels; ++i) { // you have to pass in the same number of valid pointers as numChannels jassert (dataToReferTo[i] != nullptr); channels[i] = dataToReferTo[i] + offset; } channels [numChannels] = nullptr; isClear = false; } AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) noexcept { if (this != &other) { setSize (other.getNumChannels(), other.getNumSamples(), false, false, false); if (other.isClear) { clear(); } else { isClear = false; for (int i = 0; i < numChannels; ++i) FloatVectorOperations::copy (channels[i], other.channels[i], size); } } return *this; } AudioSampleBuffer::~AudioSampleBuffer() noexcept { } void AudioSampleBuffer::setSize (const int newNumChannels, const int newNumSamples, const bool keepExistingContent, const bool clearExtraSpace, const bool avoidReallocating) noexcept { jassert (newNumChannels >= 0); jassert (newNumSamples >= 0); if (newNumSamples != size || newNumChannels != numChannels) { const size_t allocatedSamplesPerChannel = ((size_t) newNumSamples + 3) & ~3u; const size_t channelListSize = ((sizeof (float*) * (size_t) (newNumChannels + 1)) + 15) & ~15u; const size_t newTotalBytes = ((size_t) newNumChannels * (size_t) allocatedSamplesPerChannel * sizeof (float)) + channelListSize + 32; if (keepExistingContent) { HeapBlock newData; newData.allocate (newTotalBytes, clearExtraSpace || isClear); const size_t numSamplesToCopy = (size_t) jmin (newNumSamples, size); float** const newChannels = reinterpret_cast (newData.getData()); float* newChan = reinterpret_cast (newData + channelListSize); for (int j = 0; j < newNumChannels; ++j) { newChannels[j] = newChan; newChan += allocatedSamplesPerChannel; } if (! isClear) { const int numChansToCopy = jmin (numChannels, newNumChannels); for (int i = 0; i < numChansToCopy; ++i) FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy); } allocatedData.swapWith (newData); allocatedBytes = newTotalBytes; channels = newChannels; } else { if (avoidReallocating && allocatedBytes >= newTotalBytes) { if (clearExtraSpace || isClear) allocatedData.clear (newTotalBytes); } else { allocatedBytes = newTotalBytes; allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); channels = reinterpret_cast (allocatedData.getData()); } float* chan = reinterpret_cast (allocatedData + channelListSize); for (int i = 0; i < newNumChannels; ++i) { channels[i] = chan; chan += allocatedSamplesPerChannel; } } channels [newNumChannels] = 0; size = newNumSamples; numChannels = newNumChannels; } } void AudioSampleBuffer::clear() noexcept { if (! isClear) { for (int i = 0; i < numChannels; ++i) FloatVectorOperations::clear (channels[i], size); isClear = true; } } void AudioSampleBuffer::clear (const int startSample, const int numSamples) noexcept { jassert (startSample >= 0 && startSample + numSamples <= size); if (! isClear) { if (startSample == 0 && numSamples == size) isClear = true; for (int i = 0; i < numChannels; ++i) FloatVectorOperations::clear (channels[i] + startSample, numSamples); } } void AudioSampleBuffer::clear (const int channel, const int startSample, const int numSamples) noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); if (! isClear) FloatVectorOperations::clear (channels [channel] + startSample, numSamples); } float AudioSampleBuffer::getSample (int channel, int index) const noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (isPositiveAndBelow (index, size)); return *(channels [channel] + index); } void AudioSampleBuffer::setSample (int channel, int index, float newValue) noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (isPositiveAndBelow (index, size)); *(channels [channel] + index) = newValue; isClear = false; } void AudioSampleBuffer::addSample (int channel, int index, float valueToAdd) noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (isPositiveAndBelow (index, size)); *(channels [channel] + index) += valueToAdd; isClear = false; } void AudioSampleBuffer::applyGain (const int channel, const int startSample, int numSamples, const float gain) noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); if (gain != 1.0f && ! isClear) { float* const d = channels [channel] + startSample; if (gain == 0.0f) FloatVectorOperations::clear (d, numSamples); else FloatVectorOperations::multiply (d, gain, numSamples); } } void AudioSampleBuffer::applyGainRamp (const int channel, const int startSample, int numSamples, float startGain, float endGain) noexcept { if (! isClear) { if (startGain == endGain) { applyGain (channel, startSample, numSamples, startGain); } else { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); const float increment = (endGain - startGain) / numSamples; float* d = channels [channel] + startSample; while (--numSamples >= 0) { *d++ *= startGain; startGain += increment; } } } } void AudioSampleBuffer::applyGain (int startSample, int numSamples, float gain) noexcept { for (int i = 0; i < numChannels; ++i) applyGain (i, startSample, numSamples, gain); } void AudioSampleBuffer::applyGain (const float gain) noexcept { applyGain (0, size, gain); } void AudioSampleBuffer::applyGainRamp (int startSample, int numSamples, float startGain, float endGain) noexcept { for (int i = 0; i < numChannels; ++i) applyGainRamp (i, startSample, numSamples, startGain, endGain); } void AudioSampleBuffer::addFrom (const int destChannel, const int destStartSample, const AudioSampleBuffer& source, const int sourceChannel, const int sourceStartSample, int numSamples, const float gain) noexcept { jassert (&source != this || sourceChannel != destChannel); jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); if (gain != 0.0f && numSamples > 0 && ! source.isClear) { float* const d = channels [destChannel] + destStartSample; const float* const s = source.channels [sourceChannel] + sourceStartSample; if (isClear) { isClear = false; if (gain != 1.0f) FloatVectorOperations::copyWithMultiply (d, s, gain, numSamples); else FloatVectorOperations::copy (d, s, numSamples); } else { if (gain != 1.0f) FloatVectorOperations::addWithMultiply (d, s, gain, numSamples); else FloatVectorOperations::add (d, s, numSamples); } } } void AudioSampleBuffer::addFrom (const int destChannel, const int destStartSample, const float* source, int numSamples, const float gain) noexcept { jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); if (gain != 0.0f && numSamples > 0) { float* const d = channels [destChannel] + destStartSample; if (isClear) { isClear = false; if (gain != 1.0f) FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); else FloatVectorOperations::copy (d, source, numSamples); } else { if (gain != 1.0f) FloatVectorOperations::addWithMultiply (d, source, gain, numSamples); else FloatVectorOperations::add (d, source, numSamples); } } } void AudioSampleBuffer::addFromWithRamp (const int destChannel, const int destStartSample, const float* source, int numSamples, float startGain, const float endGain) noexcept { jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); if (startGain == endGain) { addFrom (destChannel, destStartSample, source, numSamples, startGain); } else { if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f)) { isClear = false; const float increment = (endGain - startGain) / numSamples; float* d = channels [destChannel] + destStartSample; while (--numSamples >= 0) { *d++ += startGain * *source++; startGain += increment; } } } } void AudioSampleBuffer::copyFrom (const int destChannel, const int destStartSample, const AudioSampleBuffer& source, const int sourceChannel, const int sourceStartSample, int numSamples) noexcept { jassert (&source != this || sourceChannel != destChannel); jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); if (numSamples > 0) { if (source.isClear) { if (! isClear) FloatVectorOperations::clear (channels [destChannel] + destStartSample, numSamples); } else { isClear = false; FloatVectorOperations::copy (channels [destChannel] + destStartSample, source.channels [sourceChannel] + sourceStartSample, numSamples); } } } void AudioSampleBuffer::copyFrom (const int destChannel, const int destStartSample, const float* source, int numSamples) noexcept { jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); if (numSamples > 0) { isClear = false; FloatVectorOperations::copy (channels [destChannel] + destStartSample, source, numSamples); } } void AudioSampleBuffer::copyFrom (const int destChannel, const int destStartSample, const float* source, int numSamples, const float gain) noexcept { jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); if (numSamples > 0) { float* const d = channels [destChannel] + destStartSample; if (gain != 1.0f) { if (gain == 0) { if (! isClear) FloatVectorOperations::clear (d, numSamples); } else { isClear = false; FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); } } else { isClear = false; FloatVectorOperations::copy (d, source, numSamples); } } } void AudioSampleBuffer::copyFromWithRamp (const int destChannel, const int destStartSample, const float* source, int numSamples, float startGain, float endGain) noexcept { jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); if (startGain == endGain) { copyFrom (destChannel, destStartSample, source, numSamples, startGain); } else { if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f)) { isClear = false; const float increment = (endGain - startGain) / numSamples; float* d = channels [destChannel] + destStartSample; while (--numSamples >= 0) { *d++ = startGain * *source++; startGain += increment; } } } } void AudioSampleBuffer::reverse (int channel, int startSample, int numSamples) const noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); if (! isClear) std::reverse (channels[channel] + startSample, channels[channel] + startSample + numSamples); } void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept { for (int i = 0; i < numChannels; ++i) reverse (i, startSample, numSamples); } Range AudioSampleBuffer::findMinMax (const int channel, const int startSample, int numSamples) const noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); if (isClear) return Range(); return FloatVectorOperations::findMinAndMax (channels [channel] + startSample, numSamples); } float AudioSampleBuffer::getMagnitude (const int channel, const int startSample, const int numSamples) const noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); if (isClear) return 0.0f; const Range r (findMinMax (channel, startSample, numSamples)); return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd()); } float AudioSampleBuffer::getMagnitude (int startSample, int numSamples) const noexcept { float mag = 0.0f; if (! isClear) for (int i = 0; i < numChannels; ++i) mag = jmax (mag, getMagnitude (i, startSample, numSamples)); return mag; } float AudioSampleBuffer::getRMSLevel (const int channel, const int startSample, const int numSamples) const noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear) return 0.0f; const float* const data = channels [channel] + startSample; double sum = 0.0; for (int i = 0; i < numSamples; ++i) { const float sample = data [i]; sum += sample * sample; } return (float) std::sqrt (sum / numSamples); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h000066400000000000000000000576561320201440200334320ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED #define JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED //============================================================================== /** A multi-channel buffer of 32-bit floating point audio samples. */ class JUCE_API AudioSampleBuffer { public: //============================================================================== /** Creates an empty buffer with 0 channels and 0 length. */ AudioSampleBuffer() noexcept; //============================================================================== /** Creates a buffer with a specified number of channels and samples. The contents of the buffer will initially be undefined, so use clear() to set all the samples to zero. The buffer will allocate its memory internally, and this will be released when the buffer is deleted. If the memory can't be allocated, this will throw a std::bad_alloc exception. */ AudioSampleBuffer (int numChannels, int numSamples) noexcept; /** Creates a buffer using a pre-allocated block of memory. Note that if the buffer is resized or its number of channels is changed, it will re-allocate memory internally and copy the existing data to this new area, so it will then stop directly addressing this memory. @param dataToReferTo a pre-allocated array containing pointers to the data for each channel that should be used by this buffer. The buffer will only refer to this memory, it won't try to delete it when the buffer is deleted or resized. @param numChannels the number of channels to use - this must correspond to the number of elements in the array passed in @param numSamples the number of samples to use - this must correspond to the size of the arrays passed in */ AudioSampleBuffer (float* const* dataToReferTo, int numChannels, int numSamples) noexcept; /** Creates a buffer using a pre-allocated block of memory. Note that if the buffer is resized or its number of channels is changed, it will re-allocate memory internally and copy the existing data to this new area, so it will then stop directly addressing this memory. @param dataToReferTo a pre-allocated array containing pointers to the data for each channel that should be used by this buffer. The buffer will only refer to this memory, it won't try to delete it when the buffer is deleted or resized. @param numChannels the number of channels to use - this must correspond to the number of elements in the array passed in @param startSample the offset within the arrays at which the data begins @param numSamples the number of samples to use - this must correspond to the size of the arrays passed in */ AudioSampleBuffer (float* const* dataToReferTo, int numChannels, int startSample, int numSamples) noexcept; /** Copies another buffer. This buffer will make its own copy of the other's data, unless the buffer was created using an external data buffer, in which case boths buffers will just point to the same shared block of data. */ AudioSampleBuffer (const AudioSampleBuffer&) noexcept; /** Copies another buffer onto this one. This buffer's size will be changed to that of the other buffer. */ AudioSampleBuffer& operator= (const AudioSampleBuffer&) noexcept; /** Destructor. This will free any memory allocated by the buffer. */ ~AudioSampleBuffer() noexcept; //============================================================================== /** Returns the number of channels of audio data that this buffer contains. @see getSampleData */ int getNumChannels() const noexcept { return numChannels; } /** Returns the number of samples allocated in each of the buffer's channels. @see getSampleData */ int getNumSamples() const noexcept { return size; } /** Returns a pointer to an array of read-only samples in one of the buffer's channels. For speed, this doesn't check whether the channel number is out of range, so be careful when using it! If you need to write to the data, do NOT call this method and const_cast the result! Instead, you must call getWritePointer so that the buffer knows you're planning on modifying the data. */ const float* getReadPointer (int channelNumber) const noexcept { jassert (isPositiveAndBelow (channelNumber, numChannels)); return channels [channelNumber]; } /** Returns a pointer to an array of read-only samples in one of the buffer's channels. For speed, this doesn't check whether the channel number or index are out of range, so be careful when using it! If you need to write to the data, do NOT call this method and const_cast the result! Instead, you must call getWritePointer so that the buffer knows you're planning on modifying the data. */ const float* getReadPointer (int channelNumber, int sampleIndex) const noexcept { jassert (isPositiveAndBelow (channelNumber, numChannels)); jassert (isPositiveAndBelow (sampleIndex, size)); return channels [channelNumber] + sampleIndex; } /** Returns a writeable pointer to one of the buffer's channels. For speed, this doesn't check whether the channel number is out of range, so be careful when using it! Note that if you're not planning on writing to the data, you should always use getReadPointer instead. */ float* getWritePointer (int channelNumber) noexcept { jassert (isPositiveAndBelow (channelNumber, numChannels)); isClear = false; return channels [channelNumber]; } /** Returns a writeable pointer to one of the buffer's channels. For speed, this doesn't check whether the channel number or index are out of range, so be careful when using it! Note that if you're not planning on writing to the data, you should use getReadPointer instead. */ float* getWritePointer (int channelNumber, int sampleIndex) noexcept { jassert (isPositiveAndBelow (channelNumber, numChannels)); jassert (isPositiveAndBelow (sampleIndex, size)); isClear = false; return channels [channelNumber] + sampleIndex; } /** Returns an array of pointers to the channels in the buffer. Don't modify any of the pointers that are returned, and bear in mind that these will become invalid if the buffer is resized. */ const float** getArrayOfReadPointers() const noexcept { return const_cast (channels); } /** Returns an array of pointers to the channels in the buffer. Don't modify any of the pointers that are returned, and bear in mind that these will become invalid if the buffer is resized. */ float** getArrayOfWritePointers() noexcept { isClear = false; return channels; } //============================================================================== /** Changes the buffer's size or number of channels. This can expand or contract the buffer's length, and add or remove channels. If keepExistingContent is true, it will try to preserve as much of the old data as it can in the new buffer. If clearExtraSpace is true, then any extra channels or space that is allocated will be also be cleared. If false, then this space is left uninitialised. If avoidReallocating is true, then changing the buffer's size won't reduce the amount of memory that is currently allocated (but it will still increase it if the new size is bigger than the amount it currently has). If this is false, then a new allocation will be done so that the buffer uses takes up the minimum amount of memory that it needs. If the required memory can't be allocated, this will throw a std::bad_alloc exception. */ void setSize (int newNumChannels, int newNumSamples, bool keepExistingContent = false, bool clearExtraSpace = false, bool avoidReallocating = false) noexcept; /** Makes this buffer point to a pre-allocated set of channel data arrays. There's also a constructor that lets you specify arrays like this, but this lets you change the channels dynamically. Note that if the buffer is resized or its number of channels is changed, it will re-allocate memory internally and copy the existing data to this new area, so it will then stop directly addressing this memory. @param dataToReferTo a pre-allocated array containing pointers to the data for each channel that should be used by this buffer. The buffer will only refer to this memory, it won't try to delete it when the buffer is deleted or resized. @param numChannels the number of channels to use - this must correspond to the number of elements in the array passed in @param numSamples the number of samples to use - this must correspond to the size of the arrays passed in */ void setDataToReferTo (float** dataToReferTo, int numChannels, int numSamples) noexcept; //============================================================================== /** Clears all the samples in all channels. */ void clear() noexcept; /** Clears a specified region of all the channels. For speed, this doesn't check whether the channel and sample number are in-range, so be careful! */ void clear (int startSample, int numSamples) noexcept; /** Clears a specified region of just one channel. For speed, this doesn't check whether the channel and sample number are in-range, so be careful! */ void clear (int channel, int startSample, int numSamples) noexcept; /** Returns true if the buffer has been entirely cleared. Note that this does not actually measure the contents of the buffer - it simply returns a flag that is set when the buffer is cleared, and which is reset whenever functions like getWritePointer() are invoked. That means the method does not take any time, but it may return false negatives when in fact the buffer is still empty. */ bool hasBeenCleared() const noexcept { return isClear; } //============================================================================== /** Returns a sample from the buffer. The channel and index are not checked - they are expected to be in-range. If not, an assertion will be thrown, but in a release build, you're into 'undefined behaviour' territory. */ float getSample (int channel, int sampleIndex) const noexcept; /** Sets a sample in the buffer. The channel and index are not checked - they are expected to be in-range. If not, an assertion will be thrown, but in a release build, you're into 'undefined behaviour' territory. */ void setSample (int destChannel, int destSample, float newValue) noexcept; /** Adds a value to a sample in the buffer. The channel and index are not checked - they are expected to be in-range. If not, an assertion will be thrown, but in a release build, you're into 'undefined behaviour' territory. */ void addSample (int destChannel, int destSample, float valueToAdd) noexcept; /** Applies a gain multiple to a region of one channel. For speed, this doesn't check whether the channel and sample number are in-range, so be careful! */ void applyGain (int channel, int startSample, int numSamples, float gain) noexcept; /** Applies a gain multiple to a region of all the channels. For speed, this doesn't check whether the sample numbers are in-range, so be careful! */ void applyGain (int startSample, int numSamples, float gain) noexcept; /** Applies a gain multiple to all the audio data. */ void applyGain (float gain) noexcept; /** Applies a range of gains to a region of a channel. The gain that is applied to each sample will vary from startGain on the first sample to endGain on the last Sample, so it can be used to do basic fades. For speed, this doesn't check whether the sample numbers are in-range, so be careful! */ void applyGainRamp (int channel, int startSample, int numSamples, float startGain, float endGain) noexcept; /** Applies a range of gains to a region of all channels. The gain that is applied to each sample will vary from startGain on the first sample to endGain on the last Sample, so it can be used to do basic fades. For speed, this doesn't check whether the sample numbers are in-range, so be careful! */ void applyGainRamp (int startSample, int numSamples, float startGain, float endGain) noexcept; /** Adds samples from another buffer to this one. @param destChannel the channel within this buffer to add the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to add from @param sourceChannel the channel within the source buffer to read from @param sourceStartSample the offset within the source buffer's channel to start reading samples from @param numSamples the number of samples to process @param gainToApplyToSource an optional gain to apply to the source samples before they are added to this buffer's samples @see copyFrom */ void addFrom (int destChannel, int destStartSample, const AudioSampleBuffer& source, int sourceChannel, int sourceStartSample, int numSamples, float gainToApplyToSource = 1.0f) noexcept; /** Adds samples from an array of floats to one of the channels. @param destChannel the channel within this buffer to add the samples to @param destStartSample the start sample within this buffer's channel @param source the source data to use @param numSamples the number of samples to process @param gainToApplyToSource an optional gain to apply to the source samples before they are added to this buffer's samples @see copyFrom */ void addFrom (int destChannel, int destStartSample, const float* source, int numSamples, float gainToApplyToSource = 1.0f) noexcept; /** Adds samples from an array of floats, applying a gain ramp to them. @param destChannel the channel within this buffer to add the samples to @param destStartSample the start sample within this buffer's channel @param source the source data to use @param numSamples the number of samples to process @param startGain the gain to apply to the first sample (this is multiplied with the source samples before they are added to this buffer) @param endGain the gain to apply to the final sample. The gain is linearly interpolated between the first and last samples. */ void addFromWithRamp (int destChannel, int destStartSample, const float* source, int numSamples, float startGain, float endGain) noexcept; /** Copies samples from another buffer to this one. @param destChannel the channel within this buffer to copy the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to read from @param sourceChannel the channel within the source buffer to read from @param sourceStartSample the offset within the source buffer's channel to start reading samples from @param numSamples the number of samples to process @see addFrom */ void copyFrom (int destChannel, int destStartSample, const AudioSampleBuffer& source, int sourceChannel, int sourceStartSample, int numSamples) noexcept; /** Copies samples from an array of floats into one of the channels. @param destChannel the channel within this buffer to copy the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to read from @param numSamples the number of samples to process @see addFrom */ void copyFrom (int destChannel, int destStartSample, const float* source, int numSamples) noexcept; /** Copies samples from an array of floats into one of the channels, applying a gain to it. @param destChannel the channel within this buffer to copy the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to read from @param numSamples the number of samples to process @param gain the gain to apply @see addFrom */ void copyFrom (int destChannel, int destStartSample, const float* source, int numSamples, float gain) noexcept; /** Copies samples from an array of floats into one of the channels, applying a gain ramp. @param destChannel the channel within this buffer to copy the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to read from @param numSamples the number of samples to process @param startGain the gain to apply to the first sample (this is multiplied with the source samples before they are copied to this buffer) @param endGain the gain to apply to the final sample. The gain is linearly interpolated between the first and last samples. @see addFrom */ void copyFromWithRamp (int destChannel, int destStartSample, const float* source, int numSamples, float startGain, float endGain) noexcept; /** Returns a Range indicating the lowest and highest sample values in a given section. @param channel the channel to read from @param startSample the start sample within the channel @param numSamples the number of samples to check */ Range findMinMax (int channel, int startSample, int numSamples) const noexcept; /** Finds the highest absolute sample value within a region of a channel. */ float getMagnitude (int channel, int startSample, int numSamples) const noexcept; /** Finds the highest absolute sample value within a region on all channels. */ float getMagnitude (int startSample, int numSamples) const noexcept; /** Returns the root mean squared level for a region of a channel. */ float getRMSLevel (int channel, int startSample, int numSamples) const noexcept; /** Reverses a part of a channel. */ void reverse (int channel, int startSample, int numSamples) const noexcept; /** Reverses a part of the buffer. */ void reverse (int startSample, int numSamples) const noexcept; //============================================================================== #ifndef DOXYGEN // Note that these methods have now been replaced by getReadPointer() and getWritePointer() JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel) const, { return getReadPointer (channel); }) JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel, int index) const, { return getReadPointer (channel, index); }) JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel), { return getWritePointer (channel); }) JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel, int index), { return getWritePointer (channel, index); }) // These have been replaced by getArrayOfReadPointers() and getArrayOfWritePointers() JUCE_DEPRECATED_WITH_BODY (const float** getArrayOfChannels() const, { return getArrayOfReadPointers(); }) JUCE_DEPRECATED_WITH_BODY (float** getArrayOfChannels(), { return getArrayOfWritePointers(); }) #endif private: //============================================================================== int numChannels, size; size_t allocatedBytes; float** channels; HeapBlock allocatedData; float* preallocatedChannelSpace [32]; bool isClear; void allocateData(); void allocateChannels (float* const*, int offset); JUCE_LEAK_DETECTOR (AudioSampleBuffer) }; #endif // JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED juce_FloatVectorOperations.cpp000066400000000000000000001477641320201440200346450ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/buffers/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace FloatVectorHelpers { #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); #define JUCE_INCREMENT_SRC1_SRC2_DEST dest += (16 / sizeof (*dest)); src1 += (16 / sizeof (*dest)); src2 += (16 / sizeof (*dest)); #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); #if JUCE_USE_SSE_INTRINSICS static bool sse2Present = false; static bool isSSE2Available() noexcept { if (sse2Present) return true; sse2Present = SystemStats::hasSSE2(); return sse2Present; } inline static bool isAligned (const void* p) noexcept { return (((pointer_sized_int) p) & 15) == 0; } struct BasicOps32 { typedef float Type; typedef __m128 ParallelType; typedef __m128 IntegerType; enum { numParallel = 4 }; // Integer and parallel types are the same for SSE. On neon they have different types static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); } static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); } static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); } static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_ps (dest, a); } static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_ps (dest, a); } static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_ps (a, b); } static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_ps (a, b); } static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_ps (a, b); } static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); } static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_ps (a, b); } static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_ps (a, b); } static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_ps (a, b); } static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_ps (a, b); } static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } }; struct BasicOps64 { typedef double Type; typedef __m128d ParallelType; typedef __m128d IntegerType; enum { numParallel = 2 }; // Integer and parallel types are the same for SSE. On neon they have different types static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); } static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); } static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); } static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_pd (dest, a); } static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_pd (dest, a); } static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_pd (a, b); } static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_pd (a, b); } static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_pd (a, b); } static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); } static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_pd (a, b); } static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_pd (a, b); } static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_pd (a, b); } static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_pd (a, b); } static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); } static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); } }; #define JUCE_BEGIN_VEC_OP \ typedef FloatVectorHelpers::ModeType::Mode Mode; \ if (FloatVectorHelpers::isSSE2Available()) \ { \ const int numLongOps = num / Mode::numParallel; #define JUCE_FINISH_VEC_OP(normalOp) \ num &= (Mode::numParallel - 1); \ if (num == 0) return; \ } \ for (int i = 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ if (FloatVectorHelpers::isAligned (dest)) JUCE_VEC_LOOP (vecOp, dummy, Mode::loadA, Mode::storeA, locals, JUCE_INCREMENT_DEST) \ else JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ JUCE_FINISH_VEC_OP (normalOp) #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ if (FloatVectorHelpers::isAligned (dest)) \ { \ if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ }\ else \ { \ if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ } \ JUCE_FINISH_VEC_OP (normalOp) #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ if (FloatVectorHelpers::isAligned (dest)) \ { \ if (FloatVectorHelpers::isAligned (src1)) \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeA, locals, increment) \ } \ else \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeA, locals, increment) \ } \ } \ else \ { \ if (FloatVectorHelpers::isAligned (src1)) \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeU, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ } \ else \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeU, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ } \ } \ JUCE_FINISH_VEC_OP (normalOp) #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ if (FloatVectorHelpers::isAligned (dest)) \ { \ if (FloatVectorHelpers::isAligned (src1)) \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ } \ else \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ } \ } \ else \ { \ if (FloatVectorHelpers::isAligned (src1)) \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ } \ else \ { \ if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ } \ } \ JUCE_FINISH_VEC_OP (normalOp) //============================================================================== #elif JUCE_USE_ARM_NEON struct BasicOps32 { typedef float Type; typedef float32x4_t ParallelType; typedef uint32x4_t IntegerType; enum { numParallel = 4 }; static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); } static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); } static forcedinline void storeA (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); } static forcedinline void storeU (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); } static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return vaddq_f32 (a, b); } static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return vsubq_f32 (a, b); } static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return vmulq_f32 (a, b); } static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); } static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (vandq_u32 (toint (a), toint (b))); } static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt (vbicq_u32 (toint (a), toint (b))); } static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (vorrq_u32 (toint (a), toint (b))); } static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (veorq_u32 (toint (a), toint (b))); } static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } }; struct BasicOps64 { typedef double Type; typedef double ParallelType; typedef uint64 IntegerType; enum { numParallel = 1 }; static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } static forcedinline ParallelType load1 (Type v) noexcept { return v; } static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; } static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; } static forcedinline void storeA (Type* dest, ParallelType a) noexcept { *dest = a; } static forcedinline void storeU (Type* dest, ParallelType a) noexcept { *dest = a; } static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return a + b; } static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return a - b; } static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return a * b; } static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); } static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); } static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) & toint (b)); } static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt ((~toint (a)) & toint (b)); } static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) | toint (b)); } static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) ^ toint (b)); } static forcedinline Type max (ParallelType a) noexcept { return a; } static forcedinline Type min (ParallelType a) noexcept { return a; } }; #define JUCE_BEGIN_VEC_OP \ typedef FloatVectorHelpers::ModeType::Mode Mode; \ if (Mode::numParallel > 1) \ { \ const int numLongOps = num / Mode::numParallel; #define JUCE_FINISH_VEC_OP(normalOp) \ num &= (Mode::numParallel - 1); \ if (num == 0) return; \ } \ for (int i = 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ JUCE_FINISH_VEC_OP (normalOp) #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ JUCE_FINISH_VEC_OP (normalOp) #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ JUCE_FINISH_VEC_OP (normalOp) #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ JUCE_BEGIN_VEC_OP \ setupOp \ JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ JUCE_FINISH_VEC_OP (normalOp) //============================================================================== #else #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ for (int i = 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ for (int i = 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ for (int i = 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ for (int i = 0; i < num; ++i) normalOp; #endif //============================================================================== #define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \ for (int i = 0; i < numLongOps; ++i) \ { \ locals (srcLoad, dstLoad); \ dstStore (dest, vecOp); \ increment; \ } #define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \ for (int i = 0; i < numLongOps; ++i) \ { \ locals (src1Load, src2Load); \ dstStore (dest, vecOp); \ increment; \ } #define JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD(vecOp, src1Load, src2Load, dstLoad, dstStore, locals, increment) \ for (int i = 0; i < numLongOps; ++i) \ { \ locals (src1Load, src2Load, dstLoad); \ dstStore (dest, vecOp); \ increment; \ } #define JUCE_LOAD_NONE(srcLoad, dstLoad) #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); #define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2); #define JUCE_LOAD_SRC1_SRC2_DEST(src1Load, src2Load, dstLoad) const Mode::ParallelType d = dstLoad (dest), s1 = src1Load (src1), s2 = src2Load (src2); #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON template struct ModeType { typedef BasicOps32 Mode; }; template<> struct ModeType<8> { typedef BasicOps64 Mode; }; template struct MinMax { typedef typename Mode::Type Type; typedef typename Mode::ParallelType ParallelType; static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept { int numLongOps = num / Mode::numParallel; #if JUCE_USE_SSE_INTRINSICS if (numLongOps > 1 && isSSE2Available()) #else if (numLongOps > 1) #endif { ParallelType val; #if ! JUCE_USE_ARM_NEON if (isAligned (src)) { val = Mode::loadA (src); if (isMinimum) { while (--numLongOps > 0) { src += Mode::numParallel; val = Mode::min (val, Mode::loadA (src)); } } else { while (--numLongOps > 0) { src += Mode::numParallel; val = Mode::max (val, Mode::loadA (src)); } } } else #endif { val = Mode::loadU (src); if (isMinimum) { while (--numLongOps > 0) { src += Mode::numParallel; val = Mode::min (val, Mode::loadU (src)); } } else { while (--numLongOps > 0) { src += Mode::numParallel; val = Mode::max (val, Mode::loadU (src)); } } } Type result = isMinimum ? Mode::min (val) : Mode::max (val); num &= (Mode::numParallel - 1); src += Mode::numParallel; for (int i = 0; i < num; ++i) result = isMinimum ? jmin (result, src[i]) : jmax (result, src[i]); return result; } return isMinimum ? juce::findMinimum (src, num) : juce::findMaximum (src, num); } static Range findMinAndMax (const Type* src, int num) noexcept { int numLongOps = num / Mode::numParallel; #if JUCE_USE_SSE_INTRINSICS if (numLongOps > 1 && isSSE2Available()) #else if (numLongOps > 1) #endif { ParallelType mn, mx; #if ! JUCE_USE_ARM_NEON if (isAligned (src)) { mn = Mode::loadA (src); mx = mn; while (--numLongOps > 0) { src += Mode::numParallel; const ParallelType v = Mode::loadA (src); mn = Mode::min (mn, v); mx = Mode::max (mx, v); } } else #endif { mn = Mode::loadU (src); mx = mn; while (--numLongOps > 0) { src += Mode::numParallel; const ParallelType v = Mode::loadU (src); mn = Mode::min (mn, v); mx = Mode::max (mx, v); } } Range result (Mode::min (mn), Mode::max (mx)); num &= (Mode::numParallel - 1); src += Mode::numParallel; for (int i = 0; i < num; ++i) result = result.getUnionWith (src[i]); return result; } return Range::findMinAndMax (src, num); } }; #endif } //============================================================================== void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vclr (dest, 1, (size_t) num); #else zeromem (dest, (size_t) num * sizeof (float)); #endif } void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vclrD (dest, 1, (size_t) num); #else zeromem (dest, (size_t) num * sizeof (double)); #endif } void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vfill (&valueToFill, dest, 1, (size_t) num); #else JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, const Mode::ParallelType val = Mode::load1 (valueToFill);) #endif } void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vfillD (&valueToFill, dest, 1, (size_t) num); #else JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, const Mode::ParallelType val = Mode::load1 (valueToFill);) #endif } void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, int num) noexcept { memcpy (dest, src, (size_t) num * sizeof (float)); } void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept { memcpy (dest, src, (size_t) num * sizeof (double)); } void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, const Mode::ParallelType amountToAdd = Mode::load1 (amount);) #endif } void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept { JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, const Mode::ParallelType amountToAdd = Mode::load1 (amount);) } void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float* src, float amount, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsadd (src, 1, &amount, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType am = Mode::load1 (amount);) #endif } void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double* src, double amount, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsaddD (src, 1, &amount, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType am = Mode::load1 (amount);) #endif } void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src1, const float* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src1, const double* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src1, const float* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src1, const double* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) } void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vma ((float*) src1, 1, (float*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), JUCE_LOAD_SRC1_SRC2_DEST, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmaD ((double*) src1, 1, (double*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), JUCE_LOAD_SRC1_SRC2_DEST, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) } void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) } void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); #else copyWithMultiply (dest, src, -1.0f, num); #endif } void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); #else copyWithMultiply (dest, src, -1.0f, num); #endif } void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); #else union {float f; uint32 i;} signMask; signMask.i = 0x7fffffffUL; JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mask = Mode::load1 (signMask.f);) #endif } void FloatVectorOperations::abs (double* dest, const double* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); #else union {double d; uint64 i;} signMask; signMask.i = 0x7fffffffffffffffULL; JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabs (src[i]), Mode::bit_and (s, mask), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mask = Mode::load1 (signMask.d);) #endif } void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept { #if JUCE_USE_ARM_NEON JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, 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, 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);) #endif } void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src, float comp, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType cmp = Mode::load1 (comp);) } void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src, double comp, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType cmp = Mode::load1 (comp);) } void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src1, const float* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmin ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src1, const double* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vminD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src, float comp, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType cmp = Mode::load1 (comp);) } void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src, double comp, int num) noexcept { JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType cmp = Mode::load1 (comp);) } void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src1, const float* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmax ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src1, const double* src2, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmaxD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) #endif } void JUCE_CALLTYPE FloatVectorOperations::clip (float* dest, const float* src, float low, float high, int num) noexcept { jassert(high >= low); #if JUCE_USE_VDSP_FRAMEWORK vDSP_vclip ((float*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) #endif } void JUCE_CALLTYPE FloatVectorOperations::clip (double* dest, const double* src, double low, double high, int num) noexcept { jassert(high >= low); #if JUCE_USE_VDSP_FRAMEWORK vDSP_vclipD ((double*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); #else JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) #endif } Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::MinMax::findMinAndMax (src, num); #else return Range::findMinAndMax (src, num); #endif } Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::MinMax::findMinAndMax (src, num); #else return Range::findMinAndMax (src, num); #endif } float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); #else return juce::findMinimum (src, num); #endif } double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); #else return juce::findMinimum (src, num); #endif } float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); #else return juce::findMaximum (src, num); #endif } double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); #else return juce::findMaximum (src, num); #endif } void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept { #if JUCE_USE_SSE_INTRINSICS if (FloatVectorHelpers::isSSE2Available()) _MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); #endif (void) shouldEnable; } //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class FloatVectorOperationsTests : public UnitTest { public: FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations") {} template struct TestRunner { static void runTest (UnitTest& u, Random random) { const int range = random.nextBool() ? 500 : 10; const int num = random.nextInt (range) + 1; HeapBlock buffer1 ((size_t) num + 16), buffer2 ((size_t) num + 16); HeapBlock buffer3 ((size_t) num + 16); #if JUCE_ARM ValueType* const data1 = buffer1; ValueType* const data2 = buffer2; int* const int1 = buffer3; #else 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)); #endif fillRandomly (random, data1, num); fillRandomly (random, data2, num); Range minMax1 (FloatVectorOperations::findMinAndMax (data1, num)); Range minMax2 (Range::findMinAndMax (data1, num)); u.expect (minMax1 == minMax2); u.expect (valuesMatch (FloatVectorOperations::findMinimum (data1, num), juce::findMinimum (data1, num))); u.expect (valuesMatch (FloatVectorOperations::findMaximum (data1, num), juce::findMaximum (data1, num))); u.expect (valuesMatch (FloatVectorOperations::findMinimum (data2, num), juce::findMinimum (data2, num))); u.expect (valuesMatch (FloatVectorOperations::findMaximum (data2, num), juce::findMaximum (data2, num))); FloatVectorOperations::clear (data1, num); u.expect (areAllValuesEqual (data1, num, 0)); FloatVectorOperations::fill (data1, (ValueType) 2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 2)); FloatVectorOperations::add (data1, (ValueType) 2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 4)); FloatVectorOperations::copy (data2, data1, num); u.expect (areAllValuesEqual (data2, num, (ValueType) 4)); FloatVectorOperations::add (data2, data1, num); u.expect (areAllValuesEqual (data2, num, (ValueType) 8)); FloatVectorOperations::copyWithMultiply (data2, data1, (ValueType) 4, num); u.expect (areAllValuesEqual (data2, num, (ValueType) 16)); FloatVectorOperations::addWithMultiply (data2, data1, (ValueType) 4, num); u.expect (areAllValuesEqual (data2, num, (ValueType) 32)); FloatVectorOperations::multiply (data1, (ValueType) 2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 8)); FloatVectorOperations::multiply (data1, data2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); FloatVectorOperations::negate (data2, data1, num); u.expect (areAllValuesEqual (data2, num, (ValueType) -256)); FloatVectorOperations::subtract (data1, data2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 512)); FloatVectorOperations::abs (data1, data2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); FloatVectorOperations::abs (data2, data1, num); u.expect (areAllValuesEqual (data2, num, (ValueType) 256)); fillRandomly (random, int1, num); doConversionTest (u, data1, data2, int1, num); FloatVectorOperations::fill (data1, (ValueType) 2, num); FloatVectorOperations::fill (data2, (ValueType) 3, num); FloatVectorOperations::addWithMultiply (data1, data1, data2, num); u.expect (areAllValuesEqual (data1, num, (ValueType) 8)); } static void doConversionTest (UnitTest& u, float* data1, float* data2, int* const int1, int num) { FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num); convertFixed (data2, int1, 2.0f, num); u.expect (buffersMatch (data1, data2, num)); } static void doConversionTest (UnitTest&, double*, double*, int*, int) {} static void fillRandomly (Random& random, ValueType* d, int num) { while (--num >= 0) *d++ = (ValueType) (random.nextDouble() * 1000.0); } static void fillRandomly (Random& random, int* d, int num) { while (--num >= 0) *d++ = random.nextInt(); } static void convertFixed (float* d, const int* s, ValueType multiplier, int num) { while (--num >= 0) *d++ = *s++ * multiplier; } static bool areAllValuesEqual (const ValueType* d, int num, ValueType target) { while (--num >= 0) if (*d++ != target) return false; return true; } static bool buffersMatch (const ValueType* d1, const ValueType* d2, int num) { while (--num >= 0) if (! valuesMatch (*d1++, *d2++)) return false; return true; } static bool valuesMatch (ValueType v1, ValueType v2) { return std::abs (v1 - v2) < std::numeric_limits::epsilon(); } }; void runTest() { beginTest ("FloatVectorOperations"); for (int i = 1000; --i >= 0;) { TestRunner::runTest (*this, getRandom()); TestRunner::runTest (*this, getRandom()); } } }; static FloatVectorOperationsTests vectorOpTests; #endif juce_FloatVectorOperations.h000066400000000000000000000273151320201440200342770ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/buffers/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_FLOATVECTOROPERATIONS_H_INCLUDED #define JUCE_FLOATVECTOROPERATIONS_H_INCLUDED //============================================================================== /** A collection of simple vector operations on arrays of floats, accelerated with SIMD instructions where possible. */ class JUCE_API FloatVectorOperations { public: //============================================================================== /** Clears a vector of floats. */ static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept; /** Clears a vector of doubles. */ static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept; /** Copies a repeated value into a vector of floats. */ static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept; /** Copies a repeated value into a vector of doubles. */ static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept; /** Copies a vector of floats. */ static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept; /** Copies a vector of doubles. */ static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept; /** Copies a vector of floats, multiplying each value by a given multiplier */ static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; /** Copies a vector of doubles, multiplying each value by a given multiplier */ static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; /** Adds a fixed value to the destination values. */ static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept; /** Adds a fixed value to the destination values. */ static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept; /** Adds a fixed value to each source value and stores it in the destination array. */ static void JUCE_CALLTYPE add (float* dest, float* src, float amount, int numValues) noexcept; /** Adds a fixed value to each source value and stores it in the destination array. */ static void JUCE_CALLTYPE add (double* dest, double* src, double amount, int numValues) noexcept; /** Adds the source values to the destination values. */ static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; /** Adds the source values to the destination values. */ static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept; /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept; /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept; /** Subtracts the source values from the destination values. */ static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; /** Subtracts the source values from the destination values. */ static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept; /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept; /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept; /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; /** Multiplies the destination values by the source values. */ static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept; /** Multiplies the destination values by the source values. */ static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept; /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept; /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept; /** Multiplies each of the destination values by a fixed multiplier. */ static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; /** Multiplies each of the destination values by a fixed multiplier. */ static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept; /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept; /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept; /** Copies a source vector to a destination, negating each value. */ static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; /** Copies a source vector to a destination, negating each value. */ static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; /** Copies a source vector to a destination, taking the absolute of each value. */ static void JUCE_CALLTYPE abs (float* dest, const float* src, int numValues) noexcept; /** Copies a source vector to a destination, taking the absolute of each value. */ static void JUCE_CALLTYPE abs (double* dest, const double* src, int numValues) noexcept; /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ static void JUCE_CALLTYPE min (float* dest, const float* src, float comp, int num) noexcept; /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ static void JUCE_CALLTYPE min (double* dest, const double* src, double comp, int num) noexcept; /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ static void JUCE_CALLTYPE min (float* dest, const float* src1, const float* src2, int num) noexcept; /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ static void JUCE_CALLTYPE min (double* dest, const double* src1, const double* src2, int num) noexcept; /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ static void JUCE_CALLTYPE max (float* dest, const float* src, float comp, int num) noexcept; /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ static void JUCE_CALLTYPE max (double* dest, const double* src, double comp, int num) noexcept; /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ static void JUCE_CALLTYPE max (float* dest, const float* src1, const float* src2, int num) noexcept; /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ static void JUCE_CALLTYPE max (double* dest, const double* src1, const double* src2, int num) noexcept; /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ static void JUCE_CALLTYPE clip (float* dest, const float* src, float low, float high, int num) noexcept; /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ static void JUCE_CALLTYPE clip (double* dest, const double* src, double low, double high, int num) noexcept; /** Finds the miniumum and maximum values in the given array. */ static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; /** Finds the miniumum and maximum values in the given array. */ static Range JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept; /** Finds the miniumum value in the given array. */ static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept; /** Finds the miniumum value in the given array. */ static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept; /** Finds the maximum value in the given array. */ static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept; /** Finds the maximum value in the given array. */ static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept; /** On Intel CPUs, this method enables or disables the SSE flush-to-zero mode. Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE */ static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; }; #endif // JUCE_FLOATVECTOROPERATIONS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/000077500000000000000000000000001320201440200266565ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h000066400000000000000000000070331320201440200315520ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DECIBELS_H_INCLUDED #define JUCE_DECIBELS_H_INCLUDED //============================================================================== /** This class contains some helpful static methods for dealing with decibel values. */ class Decibels { public: //============================================================================== /** Converts a dBFS value to its equivalent gain level. A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. Any decibel value lower than minusInfinityDb will return a gain of 0. */ template static Type decibelsToGain (const Type decibels, const Type minusInfinityDb = (Type) defaultMinusInfinitydB) { return decibels > minusInfinityDb ? std::pow ((Type) 10.0, decibels * (Type) 0.05) : Type(); } /** Converts a gain level into a dBFS value. A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. If the gain is 0 (or negative), then the method will return the value provided as minusInfinityDb. */ template static Type gainToDecibels (const Type gain, const Type minusInfinityDb = (Type) defaultMinusInfinitydB) { return gain > Type() ? jmax (minusInfinityDb, (Type) std::log10 (gain) * (Type) 20.0) : minusInfinityDb; } //============================================================================== /** Converts a decibel reading to a string, with the 'dB' suffix. If the decibel value is lower than minusInfinityDb, the return value will be "-INF dB". */ template static String toString (const Type decibels, const int decimalPlaces = 2, const Type minusInfinityDb = (Type) defaultMinusInfinitydB) { String s; if (decibels <= minusInfinityDb) { s = "-INF dB"; } else { if (decibels >= Type()) s << '+'; s << String (decibels, decimalPlaces) << " dB"; } return s; } private: //============================================================================== enum { defaultMinusInfinitydB = -100 }; Decibels(); // This class can't be instantiated, it's just a holder for static methods.. JUCE_DECLARE_NON_COPYABLE (Decibels) }; #endif // JUCE_DECIBELS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/juce_FFT.cpp000066400000000000000000000213701320201440200310120ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ // (For the moment, we'll implement a few local operators for this complex class - one // day we'll probably either have a juce complex class, or use the C++11 one) static FFT::Complex operator+ (FFT::Complex a, FFT::Complex b) noexcept { FFT::Complex c = { a.r + b.r, a.i + b.i }; return c; } static FFT::Complex operator- (FFT::Complex a, FFT::Complex b) noexcept { FFT::Complex c = { a.r - b.r, a.i - b.i }; return c; } static FFT::Complex operator* (FFT::Complex a, FFT::Complex b) noexcept { FFT::Complex c = { a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r }; return c; } static FFT::Complex& operator+= (FFT::Complex& a, FFT::Complex b) noexcept { a.r += b.r; a.i += b.i; return a; } //============================================================================== struct FFT::FFTConfig { FFTConfig (int sizeOfFFT, bool isInverse) : fftSize (sizeOfFFT), inverse (isInverse), twiddleTable ((size_t) sizeOfFFT) { for (int i = 0; i < fftSize; ++i) { const double phase = (isInverse ? 2.0 : -2.0) * double_Pi * i / fftSize; twiddleTable[i].r = (float) cos (phase); twiddleTable[i].i = (float) sin (phase); } const int root = (int) std::sqrt ((double) fftSize); int divisor = 4, n = fftSize; for (int i = 0; i < numElementsInArray (factors); ++i) { while ((n % divisor) != 0) { if (divisor == 2) divisor = 3; else if (divisor == 4) divisor = 2; else divisor += 2; if (divisor > root) divisor = n; } n /= divisor; jassert (divisor == 1 || divisor == 2 || divisor == 4); factors[i].radix = divisor; factors[i].length = n; } } void perform (const Complex* input, Complex* output) const noexcept { perform (input, output, 1, 1, factors); } const int fftSize; const bool inverse; struct Factor { int radix, length; }; Factor factors[32]; HeapBlock twiddleTable; void perform (const Complex* input, Complex* output, const int stride, const int strideIn, const Factor* facs) const noexcept { const Factor factor (*facs++); Complex* const originalOutput = output; const Complex* const outputEnd = output + factor.radix * factor.length; if (stride == 1 && factor.radix <= 5) { for (int i = 0; i < factor.radix; ++i) perform (input + stride * strideIn * i, output + i * factor.length, stride * factor.radix, strideIn, facs); butterfly (factor, output, stride); return; } if (factor.length == 1) { do { *output++ = *input; input += stride * strideIn; } while (output < outputEnd); } else { do { perform (input, output, stride * factor.radix, strideIn, facs); input += stride * strideIn; output += factor.length; } while (output < outputEnd); } butterfly (factor, originalOutput, stride); } void butterfly (const Factor factor, Complex* data, const int stride) const noexcept { switch (factor.radix) { case 1: break; case 2: butterfly2 (data, stride, factor.length); return; case 4: butterfly4 (data, stride, factor.length); return; default: jassertfalse; break; } Complex* scratch = static_cast (alloca (sizeof (Complex) * (size_t) factor.radix)); for (int i = 0; i < factor.length; ++i) { for (int k = i, q1 = 0; q1 < factor.radix; ++q1) { scratch[q1] = data[k]; k += factor.length; } for (int k = i, q1 = 0; q1 < factor.radix; ++q1) { int twiddleIndex = 0; data[k] = scratch[0]; for (int q = 1; q < factor.radix; ++q) { twiddleIndex += stride * k; if (twiddleIndex >= fftSize) twiddleIndex -= fftSize; data[k] += scratch[q] * twiddleTable[twiddleIndex]; } k += factor.length; } } } void butterfly2 (Complex* data, const int stride, const int length) const noexcept { Complex* dataEnd = data + length; const Complex* tw = twiddleTable; for (int i = length; --i >= 0;) { const Complex s (*dataEnd * *tw); tw += stride; *dataEnd++ = *data - s; *data++ += s; } } void butterfly4 (Complex* data, const int stride, const int length) const noexcept { const int lengthX2 = length * 2; const int lengthX3 = length * 3; const Complex* twiddle1 = twiddleTable; const Complex* twiddle2 = twiddle1; const Complex* twiddle3 = twiddle1; for (int i = length; --i >= 0;) { const Complex s0 = data[length] * *twiddle1; const Complex s1 = data[lengthX2] * *twiddle2; const Complex s2 = data[lengthX3] * *twiddle3; const Complex s3 = s0 + s2; const Complex s4 = s0 - s2; const Complex s5 = *data - s1; *data += s1; data[lengthX2] = *data - s3; twiddle1 += stride; twiddle2 += stride * 2; twiddle3 += stride * 3; *data += s3; if (inverse) { data[length].r = s5.r - s4.i; data[length].i = s5.i + s4.r; data[lengthX3].r = s5.r + s4.i; data[lengthX3].i = s5.i - s4.r; } else { data[length].r = s5.r + s4.i; data[length].i = s5.i - s4.r; data[lengthX3].r = s5.r - s4.i; data[lengthX3].i = s5.i + s4.r; } ++data; } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFTConfig) }; //============================================================================== FFT::FFT (int order, bool inverse) : config (new FFTConfig (1 << order, inverse)), size (1 << order) {} FFT::~FFT() {} void FFT::perform (const Complex* const input, Complex* const output) const noexcept { config->perform (input, output); } void FFT::performRealOnlyForwardTransform (float* d) const noexcept { // This can only be called on an FFT object that was created to do forward transforms. jassert (! config->inverse); Complex* const scratch = static_cast (alloca (16 + sizeof (Complex) * (size_t) size)); for (int i = 0; i < size; ++i) { scratch[i].r = d[i]; scratch[i].i = 0; } perform (scratch, reinterpret_cast (d)); } void FFT::performRealOnlyInverseTransform (float* d) const noexcept { // This can only be called on an FFT object that was created to do inverse transforms. jassert (config->inverse); Complex* const scratch = static_cast (alloca (16 + sizeof (Complex) * (size_t) size)); perform (reinterpret_cast (d), scratch); const float scaleFactor = 1.0f / size; for (int i = 0; i < size; ++i) { d[i] = scratch[i].r * scaleFactor; d[i + size] = scratch[i].i * scaleFactor; } } void FFT::performFrequencyOnlyForwardTransform (float* d) const noexcept { performRealOnlyForwardTransform (d); const int twiceSize = size * 2; for (int i = 0; i < twiceSize; i += 2) { d[i / 2] = juce_hypot (d[i], d[i + 1]); if (i >= size) { d[i] = 0; d[i + 1] = 0; } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/juce_FFT.h000066400000000000000000000070031320201440200304540ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ /** A very minimal FFT class. This is only a simple low-footprint implementation and isn't tuned for speed - it may be useful for simple applications where one of the more complex FFT libraries would be overkill. (But in the future it may end up becoming optimised of course...) The FFT class itself contains lookup tables, so there's some overhead in creating one, you should create and cache an FFT object for each size/direction of transform that you need, and re-use them to perform the actual operation. */ class JUCE_API FFT { public: /** Initialises an object for performing either a forward or inverse FFT with the given size. The the number of points the FFT will operate on will be 2 ^ order. */ FFT (int order, bool isInverse); /** Destructor. */ ~FFT(); /** A complex number, for the purposes of the FFT class. */ struct Complex { float r; /**< Real part. */ float i; /**< Imaginary part. */ }; /** Performs an out-of-place FFT, either forward or inverse depending on the mode that was passed to this object's constructor. The arrays must contain at least getSize() elements. */ void perform (const Complex* input, Complex* output) const noexcept; /** Performs an in-place forward transform on a block of real data. The size of the array passed in must be 2 * getSize(), and the first half should contain your raw input sample data. On return, the array will contain complex frequency + phase data, and can be passed to performRealOnlyInverseTransform() in order to convert it back to reals. */ void performRealOnlyForwardTransform (float* inputOutputData) const noexcept; /** Performs a reverse operation to data created in performRealOnlyForwardTransform(). The size of the array passed in must be 2 * getSize(), containing complex frequency and phase data. On return, the first half of the array will contain the reconstituted samples. */ void performRealOnlyInverseTransform (float* inputOutputData) const noexcept; /** Takes an array and simply transforms it to the frequency spectrum. This may be handy for things like frequency displays or analysis. */ void performFrequencyOnlyForwardTransform (float* inputOutputData) const noexcept; /** Returns the number of data points that this FFT was created to work with. */ int getSize() const noexcept { return size; } private: struct FFTConfig; ScopedPointer config; const int size; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFT) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp000066400000000000000000000211261320201440200321630ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_INTEL #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8f || n > 1.0e-8f)) n = 0; #else #define JUCE_SNAP_TO_ZERO(n) #endif //============================================================================== IIRCoefficients::IIRCoefficients() noexcept { zeromem (coefficients, sizeof (coefficients)); } IIRCoefficients::~IIRCoefficients() noexcept {} IIRCoefficients::IIRCoefficients (const IIRCoefficients& other) noexcept { memcpy (coefficients, other.coefficients, sizeof (coefficients)); } IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexcept { memcpy (coefficients, other.coefficients, sizeof (coefficients)); return *this; } IIRCoefficients::IIRCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) noexcept { const double a = 1.0 / c4; coefficients[0] = (float) (c1 * a); coefficients[1] = (float) (c2 * a); coefficients[2] = (float) (c3 * a); coefficients[3] = (float) (c5 * a); coefficients[4] = (float) (c6 * a); } IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, const double frequency) noexcept { jassert (sampleRate > 0); const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); const double nSquared = n * n; const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); return IIRCoefficients (c1, c1 * 2.0, c1, 1.0, c1 * 2.0 * (1.0 - nSquared), c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate, const double frequency) noexcept { const double n = std::tan (double_Pi * frequency / sampleRate); const double nSquared = n * n; const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); return IIRCoefficients (c1, c1 * -2.0, c1, 1.0, c1 * 2.0 * (nSquared - 1.0), c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate, const double cutOffFrequency, const double Q, const float gainFactor) noexcept { jassert (sampleRate > 0); jassert (Q > 0); const double A = jmax (0.0f, std::sqrt (gainFactor)); const double aminus1 = A - 1.0; const double aplus1 = A + 1.0; const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; const double coso = std::cos (omega); const double beta = std::sin (omega) * std::sqrt (A) / Q; const double aminus1TimesCoso = aminus1 * coso; return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta), A * 2.0 * (aminus1 - aplus1 * coso), A * (aplus1 - aminus1TimesCoso - beta), aplus1 + aminus1TimesCoso + beta, -2.0 * (aminus1 + aplus1 * coso), aplus1 + aminus1TimesCoso - beta); } IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate, const double cutOffFrequency, const double Q, const float gainFactor) noexcept { jassert (sampleRate > 0); jassert (Q > 0); const double A = jmax (0.0f, std::sqrt (gainFactor)); const double aminus1 = A - 1.0; const double aplus1 = A + 1.0; const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; const double coso = std::cos (omega); const double beta = std::sin (omega) * std::sqrt (A) / Q; const double aminus1TimesCoso = aminus1 * coso; return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta), A * -2.0 * (aminus1 + aplus1 * coso), A * (aplus1 + aminus1TimesCoso - beta), aplus1 - aminus1TimesCoso + beta, 2.0 * (aminus1 - aplus1 * coso), aplus1 - aminus1TimesCoso - beta); } IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate, const double centreFrequency, const double Q, const float gainFactor) noexcept { jassert (sampleRate > 0); jassert (Q > 0); const double A = jmax (0.0f, std::sqrt (gainFactor)); const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; const double alpha = 0.5 * std::sin (omega) / Q; const double c2 = -2.0 * std::cos (omega); const double alphaTimesA = alpha * A; const double alphaOverA = alpha / A; return IIRCoefficients (1.0 + alphaTimesA, c2, 1.0 - alphaTimesA, 1.0 + alphaOverA, c2, 1.0 - alphaOverA); } //============================================================================== IIRFilter::IIRFilter() noexcept : v1 (0), v2 (0), active (false) { } IIRFilter::IIRFilter (const IIRFilter& other) noexcept : v1 (0), v2 (0), active (other.active) { const SpinLock::ScopedLockType sl (other.processLock); coefficients = other.coefficients; } IIRFilter::~IIRFilter() noexcept { } //============================================================================== void IIRFilter::makeInactive() noexcept { const SpinLock::ScopedLockType sl (processLock); active = false; } void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept { const SpinLock::ScopedLockType sl (processLock); coefficients = newCoefficients; active = true; } //============================================================================== void IIRFilter::reset() noexcept { const SpinLock::ScopedLockType sl (processLock); v1 = v2 = 0; } float IIRFilter::processSingleSampleRaw (const float in) noexcept { float out = coefficients.coefficients[0] * in + v1; JUCE_SNAP_TO_ZERO (out); v1 = coefficients.coefficients[1] * in - coefficients.coefficients[3] * out + v2; v2 = coefficients.coefficients[2] * in - coefficients.coefficients[4] * out; return out; } void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept { const SpinLock::ScopedLockType sl (processLock); if (active) { const float c0 = coefficients.coefficients[0]; const float c1 = coefficients.coefficients[1]; const float c2 = coefficients.coefficients[2]; const float c3 = coefficients.coefficients[3]; const float c4 = coefficients.coefficients[4]; float lv1 = v1, lv2 = v2; for (int i = 0; i < numSamples; ++i) { const float in = samples[i]; const float out = c0 * in + lv1; samples[i] = out; lv1 = c1 * in - c3 * out + lv2; lv2 = c2 * in - c4 * out; } JUCE_SNAP_TO_ZERO (lv1); v1 = lv1; JUCE_SNAP_TO_ZERO (lv2); v2 = lv2; } } #undef JUCE_SNAP_TO_ZERO libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h000066400000000000000000000154331320201440200316340ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IIRFILTER_H_INCLUDED #define JUCE_IIRFILTER_H_INCLUDED class IIRFilter; //============================================================================== /** A set of coefficients for use in an IIRFilter object. @see IIRFilter */ class JUCE_API IIRCoefficients { public: //============================================================================== /** Creates a null set of coefficients (which will produce silence). */ IIRCoefficients() noexcept; /** Directly constructs an object from the raw coefficients. Most people will want to use the static methods instead of this, but the constructor is public to allow tinkerers to create their own custom filters! */ IIRCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) noexcept; /** Creates a copy of another filter. */ IIRCoefficients (const IIRCoefficients&) noexcept; /** Creates a copy of another filter. */ IIRCoefficients& operator= (const IIRCoefficients&) noexcept; /** Destructor. */ ~IIRCoefficients() noexcept; /** Returns the coefficients for a low-pass filter. */ static IIRCoefficients makeLowPass (double sampleRate, double frequency) noexcept; /** Returns the coefficients for a high-pass filter. */ static IIRCoefficients makeHighPass (double sampleRate, double frequency) noexcept; //============================================================================== /** Returns the coefficients for a low-pass shelf filter with variable Q and gain. The gain is a scale factor that the low frequencies are multiplied by, so values greater than 1.0 will boost the low frequencies, values less than 1.0 will attenuate them. */ static IIRCoefficients makeLowShelf (double sampleRate, double cutOffFrequency, double Q, float gainFactor) noexcept; /** Returns the coefficients for a high-pass shelf filter with variable Q and gain. The gain is a scale factor that the high frequencies are multiplied by, so values greater than 1.0 will boost the high frequencies, values less than 1.0 will attenuate them. */ static IIRCoefficients makeHighShelf (double sampleRate, double cutOffFrequency, double Q, float gainFactor) noexcept; /** Returns the coefficients for a peak filter centred around a given frequency, with a variable Q and gain. The gain is a scale factor that the centre frequencies are multiplied by, so values greater than 1.0 will boost the centre frequencies, values less than 1.0 will attenuate them. */ static IIRCoefficients makePeakFilter (double sampleRate, double centreFrequency, double Q, float gainFactor) noexcept; //============================================================================== /** The raw coefficients. You should leave these numbers alone unless you really know what you're doing. */ float coefficients[5]; }; //============================================================================== /** An IIR filter that can perform low, high, or band-pass filtering on an audio signal. @see IIRCoefficient, IIRFilterAudioSource */ class JUCE_API IIRFilter { public: //============================================================================== /** Creates a filter. Initially the filter is inactive, so will have no effect on samples that you process with it. Use the setCoefficients() method to turn it into the type of filter needed. */ IIRFilter() noexcept; /** Creates a copy of another filter. */ IIRFilter (const IIRFilter&) noexcept; /** Destructor. */ ~IIRFilter() noexcept; //============================================================================== /** Clears the filter so that any incoming data passes through unchanged. */ void makeInactive() noexcept; /** Applies a set of coefficients to this filter. */ void setCoefficients (const IIRCoefficients& newCoefficients) noexcept; /** Returns the coefficients that this filter is using. */ IIRCoefficients getCoefficients() const noexcept { return coefficients; } //============================================================================== /** Resets the filter's processing pipeline, ready to start a new stream of data. Note that this clears the processing state, but the type of filter and its coefficients aren't changed. To put a filter into an inactive state, use the makeInactive() method. */ void reset() noexcept; /** Performs the filter operation on the given set of samples. */ void processSamples (float* samples, int numSamples) noexcept; /** Processes a single sample, without any locking or checking. Use this if you need fast processing of a single value, but be aware that this isn't thread-safe in the way that processSamples() is. */ float processSingleSampleRaw (float sample) noexcept; protected: //============================================================================== SpinLock processLock; IIRCoefficients coefficients; float v1, v2; bool active; IIRFilter& operator= (const IIRFilter&); JUCE_LEAK_DETECTOR (IIRFilter) }; #endif // JUCE_IIRFILTER_H_INCLUDED juce_LagrangeInterpolator.cpp000066400000000000000000000136141320201440200344410ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace LagrangeHelpers { template struct ResampleHelper { static forcedinline void calc (float& a, float b) { a *= b * (1.0f / k); } }; template<> struct ResampleHelper <0> { static forcedinline void calc (float&, float) {} }; template static forcedinline float calcCoefficient (float input, const float offset) noexcept { ResampleHelper <0 - k>::calc (input, -2.0f - offset); ResampleHelper <1 - k>::calc (input, -1.0f - offset); ResampleHelper <2 - k>::calc (input, 0.0f - offset); ResampleHelper <3 - k>::calc (input, 1.0f - offset); ResampleHelper <4 - k>::calc (input, 2.0f - offset); return input; } static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept { return calcCoefficient<0> (inputs[4], offset) + calcCoefficient<1> (inputs[3], offset) + calcCoefficient<2> (inputs[2], offset) + calcCoefficient<3> (inputs[1], offset) + calcCoefficient<4> (inputs[0], offset); } static forcedinline void push (float* inputs, const float newValue) noexcept { inputs[4] = inputs[3]; inputs[3] = inputs[2]; inputs[2] = inputs[1]; inputs[1] = inputs[0]; inputs[0] = newValue; } } //============================================================================== LagrangeInterpolator::LagrangeInterpolator() { reset(); } LagrangeInterpolator::~LagrangeInterpolator() {} void LagrangeInterpolator::reset() noexcept { subSamplePos = 1.0; for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) lastInputSamples[i] = 0; } int LagrangeInterpolator::process (const double actualRatio, const float* in, float* out, const int numOut) noexcept { if (actualRatio == 1.0) { memcpy (out, in, (size_t) numOut * sizeof (float)); if (numOut >= 4) { const float* end = in + numOut; for (int i = 0; i < 4; ++i) lastInputSamples[i] = *--end; } else { for (int i = 0; i < numOut; ++i) LagrangeHelpers::push (lastInputSamples, in[i]); } return numOut; } const float* const originalIn = in; double pos = subSamplePos; if (actualRatio < 1.0) { for (int i = numOut; --i >= 0;) { if (pos >= 1.0) { LagrangeHelpers::push (lastInputSamples, *in++); pos -= 1.0; } *out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); pos += actualRatio; } } else { for (int i = numOut; --i >= 0;) { while (pos < actualRatio) { LagrangeHelpers::push (lastInputSamples, *in++); pos += 1.0; } pos -= actualRatio; *out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, 1.0f - (float) pos); } } subSamplePos = pos; return (int) (in - originalIn); } int LagrangeInterpolator::processAdding (const double actualRatio, const float* in, float* out, const int numOut, const float gain) noexcept { if (actualRatio == 1.0) { if (gain != 1.0f) { for (int i = 0; i < numOut; ++i) out[i] += in[i] * gain; } else { for (int i = 0; i < numOut; ++i) out[i] += in[i]; } if (numOut >= 4) { const float* end = in + numOut; for (int i = 0; i < 4; ++i) lastInputSamples[i] = *--end; } else { for (int i = 0; i < numOut; ++i) LagrangeHelpers::push (lastInputSamples, in[i]); } return numOut; } const float* const originalIn = in; double pos = subSamplePos; if (actualRatio < 1.0) { for (int i = numOut; --i >= 0;) { if (pos >= 1.0) { LagrangeHelpers::push (lastInputSamples, *in++); pos -= 1.0; } *out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); pos += actualRatio; } } else { for (int i = numOut; --i >= 0;) { while (pos < actualRatio) { LagrangeHelpers::push (lastInputSamples, *in++); pos += 1.0; } pos -= actualRatio; *out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); } } subSamplePos = pos; return (int) (in - originalIn); } juce_LagrangeInterpolator.h000066400000000000000000000075731320201440200341150ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED #define JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED //============================================================================== /** Interpolator for resampling a stream of floats using 4-point lagrange interpolation. Note that the resampler is stateful, so when there's a break in the continuity of the input stream you're feeding it, you should call reset() before feeding it any new data. And like with any other stateful filter, if you're resampling multiple channels, make sure each one uses its own LagrangeInterpolator object. */ class JUCE_API LagrangeInterpolator { public: LagrangeInterpolator(); ~LagrangeInterpolator(); /** Resets the state of the interpolator. Call this when there's a break in the continuity of the input data stream. */ void reset() noexcept; /** Resamples a stream of samples. @param speedRatio the number of input samples to use for each output sample @param inputSamples the source data to read from. This must contain at least (speedRatio * numOutputSamplesToProduce) samples. @param outputSamples the buffer to write the results into @param numOutputSamplesToProduce the number of output samples that should be created @returns the actual number of input samples that were used */ int process (double speedRatio, const float* inputSamples, float* outputSamples, int numOutputSamplesToProduce) noexcept; /** Resamples a stream of samples, adding the results to the output data with a gain. @param speedRatio the number of input samples to use for each output sample @param inputSamples the source data to read from. This must contain at least (speedRatio * numOutputSamplesToProduce) samples. @param outputSamples the buffer to write the results to - the result values will be added to any pre-existing data in this buffer after being multiplied by the gain factor @param numOutputSamplesToProduce the number of output samples that should be created @param gain a gain factor to multiply the resulting samples by before adding them to the destination buffer @returns the actual number of input samples that were used */ int processAdding (double speedRatio, const float* inputSamples, float* outputSamples, int numOutputSamplesToProduce, float gain) noexcept; private: float lastInputSamples[5]; double subSamplePos; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator) }; #endif // JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h000066400000000000000000000307531320201440200312720ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_REVERB_H_INCLUDED #define JUCE_REVERB_H_INCLUDED //============================================================================== /** Performs a simple reverb effect on a stream of audio data. This is a simple stereo reverb, based on the technique and tunings used in FreeVerb. Use setSampleRate() to prepare it, and then call processStereo() or processMono() to apply the reverb to your audio data. @see ReverbAudioSource */ class Reverb { public: //============================================================================== Reverb() { setParameters (Parameters()); setSampleRate (44100.0); } //============================================================================== /** Holds the parameters being used by a Reverb object. */ struct Parameters { Parameters() noexcept : roomSize (0.5f), damping (0.5f), wetLevel (0.33f), dryLevel (0.4f), width (1.0f), freezeMode (0) {} float roomSize; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */ float damping; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */ float wetLevel; /**< Wet level, 0 to 1.0 */ float dryLevel; /**< Dry level, 0 to 1.0 */ float width; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */ float freezeMode; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5 put the reverb into a continuous feedback loop. */ }; //============================================================================== /** Returns the reverb's current parameters. */ const Parameters& getParameters() const noexcept { return parameters; } /** Applies a new set of parameters to the reverb. Note that this doesn't attempt to lock the reverb, so if you call this in parallel with the process method, you may get artifacts. */ void setParameters (const Parameters& newParams) { const float wetScaleFactor = 3.0f; const float dryScaleFactor = 2.0f; const float wet = newParams.wetLevel * wetScaleFactor; dryGain.setValue (newParams.dryLevel * dryScaleFactor); wetGain1.setValue (0.5f * wet * (1.0f + newParams.width)); wetGain2.setValue (0.5f * wet * (1.0f - newParams.width)); gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f; parameters = newParams; updateDamping(); } //============================================================================== /** Sets the sample rate that will be used for the reverb. You must call this before the process methods, in order to tell it the correct sample rate. */ void setSampleRate (const double sampleRate) { jassert (sampleRate > 0); static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz) static const short allPassTunings[] = { 556, 441, 341, 225 }; const int stereoSpread = 23; const int intSampleRate = (int) sampleRate; for (int i = 0; i < numCombs; ++i) { comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100); comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100); } for (int i = 0; i < numAllPasses; ++i) { allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100); allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100); } const double smoothTime = 0.01; damping .reset (sampleRate, smoothTime); feedback.reset (sampleRate, smoothTime); dryGain .reset (sampleRate, smoothTime); wetGain1.reset (sampleRate, smoothTime); wetGain2.reset (sampleRate, smoothTime); } /** Clears the reverb's buffers. */ void reset() { for (int j = 0; j < numChannels; ++j) { for (int i = 0; i < numCombs; ++i) comb[j][i].clear(); for (int i = 0; i < numAllPasses; ++i) allPass[j][i].clear(); } } //============================================================================== /** Applies the reverb to two stereo channels of audio data. */ void processStereo (float* const left, float* const right, const int numSamples) noexcept { jassert (left != nullptr && right != nullptr); for (int i = 0; i < numSamples; ++i) { const float input = (left[i] + right[i]) * gain; float outL = 0, outR = 0; const float damp = damping.getNextValue(); const float feedbck = feedback.getNextValue(); for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel { outL += comb[0][j].process (input, damp, feedbck); outR += comb[1][j].process (input, damp, feedbck); } for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series { outL = allPass[0][j].process (outL); outR = allPass[1][j].process (outR); } const float dry = dryGain.getNextValue(); const float wet1 = wetGain1.getNextValue(); const float wet2 = wetGain2.getNextValue(); left[i] = outL * wet1 + outR * wet2 + left[i] * dry; right[i] = outR * wet1 + outL * wet2 + right[i] * dry; } } /** Applies the reverb to a single mono channel of audio data. */ void processMono (float* const samples, const int numSamples) noexcept { jassert (samples != nullptr); for (int i = 0; i < numSamples; ++i) { const float input = samples[i] * gain; float output = 0; const float damp = damping.getNextValue(); const float feedbck = feedback.getNextValue(); for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel output += comb[0][j].process (input, damp, feedbck); for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series output = allPass[0][j].process (output); const float dry = dryGain.getNextValue(); const float wet1 = wetGain1.getNextValue(); samples[i] = output * wet1 + samples[i] * dry; } } private: //============================================================================== static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; } void updateDamping() noexcept { const float roomScaleFactor = 0.28f; const float roomOffset = 0.7f; const float dampScaleFactor = 0.4f; if (isFrozen (parameters.freezeMode)) setDamping (0.0f, 1.0f); else setDamping (parameters.damping * dampScaleFactor, parameters.roomSize * roomScaleFactor + roomOffset); } void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept { damping.setValue (dampingToUse); feedback.setValue (roomSizeToUse); } //============================================================================== class CombFilter { public: CombFilter() noexcept : bufferSize (0), bufferIndex (0), last (0) {} void setSize (const int size) { if (size != bufferSize) { bufferIndex = 0; buffer.malloc ((size_t) size); bufferSize = size; } clear(); } void clear() noexcept { last = 0; buffer.clear ((size_t) bufferSize); } float process (const float input, const float damp, const float feedbackLevel) noexcept { const float output = buffer[bufferIndex]; last = (output * (1.0f - damp)) + (last * damp); JUCE_UNDENORMALISE (last); float temp = input + (last * feedbackLevel); JUCE_UNDENORMALISE (temp); buffer[bufferIndex] = temp; bufferIndex = (bufferIndex + 1) % bufferSize; return output; } private: HeapBlock buffer; int bufferSize, bufferIndex; float last; JUCE_DECLARE_NON_COPYABLE (CombFilter) }; //============================================================================== class AllPassFilter { public: AllPassFilter() noexcept : bufferSize (0), bufferIndex (0) {} void setSize (const int size) { if (size != bufferSize) { bufferIndex = 0; buffer.malloc ((size_t) size); bufferSize = size; } clear(); } void clear() noexcept { buffer.clear ((size_t) bufferSize); } float process (const float input) noexcept { const float bufferedValue = buffer [bufferIndex]; float temp = input + (bufferedValue * 0.5f); JUCE_UNDENORMALISE (temp); buffer [bufferIndex] = temp; bufferIndex = (bufferIndex + 1) % bufferSize; return bufferedValue - input; } private: HeapBlock buffer; int bufferSize, bufferIndex; JUCE_DECLARE_NON_COPYABLE (AllPassFilter) }; //============================================================================== class LinearSmoothedValue { public: LinearSmoothedValue() noexcept : currentValue (0), target (0), step (0), countdown (0), stepsToTarget (0) { } void reset (double sampleRate, double fadeLengthSeconds) noexcept { jassert (sampleRate > 0 && fadeLengthSeconds >= 0); stepsToTarget = (int) std::floor (fadeLengthSeconds * sampleRate); currentValue = target; countdown = 0; } void setValue (float newValue) noexcept { if (target != newValue) { target = newValue; countdown = stepsToTarget; if (countdown <= 0) currentValue = target; else step = (target - currentValue) / (float) countdown; } } float getNextValue() noexcept { if (countdown <= 0) return target; --countdown; currentValue += step; return currentValue; } private: float currentValue, target, step; int countdown, stepsToTarget; JUCE_DECLARE_NON_COPYABLE (LinearSmoothedValue) }; //============================================================================== enum { numCombs = 8, numAllPasses = 4, numChannels = 2 }; Parameters parameters; float gain; CombFilter comb [numChannels][numCombs]; AllPassFilter allPass [numChannels][numAllPasses]; LinearSmoothedValue damping, feedback, dryGain, wetGain1, wetGain2; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb) }; #endif // JUCE_REVERB_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp000066400000000000000000000064331320201440200314040ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if defined (JUCE_AUDIO_BASICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" #include "juce_audio_basics.h" #if JUCE_MINGW && ! defined (__SSE2__) #define JUCE_USE_SSE_INTRINSICS 0 #endif #ifndef JUCE_USE_SSE_INTRINSICS #define JUCE_USE_SSE_INTRINSICS 1 #endif #if ! JUCE_INTEL #undef JUCE_USE_SSE_INTRINSICS #endif #if JUCE_USE_SSE_INTRINSICS #include #endif #ifndef JUCE_USE_VDSP_FRAMEWORK #define JUCE_USE_VDSP_FRAMEWORK 1 #endif #if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #include #undef Point #else #undef JUCE_USE_VDSP_FRAMEWORK #endif #if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) #define JUCE_USE_ARM_NEON 1 #endif #if JUCE_USE_ARM_NEON #include #endif namespace juce { #include "buffers/juce_AudioDataConverters.cpp" #include "buffers/juce_AudioSampleBuffer.cpp" #include "buffers/juce_FloatVectorOperations.cpp" #include "effects/juce_IIRFilter.cpp" #include "effects/juce_LagrangeInterpolator.cpp" #include "effects/juce_FFT.cpp" #include "midi/juce_MidiBuffer.cpp" #include "midi/juce_MidiFile.cpp" #include "midi/juce_MidiKeyboardState.cpp" #include "midi/juce_MidiMessage.cpp" #include "midi/juce_MidiMessageSequence.cpp" #include "sources/juce_BufferingAudioSource.cpp" #include "sources/juce_ChannelRemappingAudioSource.cpp" #include "sources/juce_IIRFilterAudioSource.cpp" #include "sources/juce_MixerAudioSource.cpp" #include "sources/juce_ResamplingAudioSource.cpp" #include "sources/juce_ReverbAudioSource.cpp" #include "sources/juce_ToneGeneratorAudioSource.cpp" #include "synthesisers/juce_Synthesiser.cpp" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h000066400000000000000000000043621320201440200310500ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIO_BASICS_H_INCLUDED #define JUCE_AUDIO_BASICS_H_INCLUDED #include "../juce_core/juce_core.h" //============================================================================= namespace juce { #undef Complex // apparently some C libraries actually define these symbols (!) #undef Factor #include "buffers/juce_AudioDataConverters.h" #include "buffers/juce_AudioSampleBuffer.h" #include "buffers/juce_FloatVectorOperations.h" #include "effects/juce_Decibels.h" #include "effects/juce_IIRFilter.h" #include "effects/juce_LagrangeInterpolator.h" #include "effects/juce_FFT.h" #include "effects/juce_Reverb.h" #include "midi/juce_MidiMessage.h" #include "midi/juce_MidiBuffer.h" #include "midi/juce_MidiMessageSequence.h" #include "midi/juce_MidiFile.h" #include "midi/juce_MidiKeyboardState.h" #include "sources/juce_AudioSource.h" #include "sources/juce_PositionableAudioSource.h" #include "sources/juce_BufferingAudioSource.h" #include "sources/juce_ChannelRemappingAudioSource.h" #include "sources/juce_IIRFilterAudioSource.h" #include "sources/juce_MixerAudioSource.h" #include "sources/juce_ResamplingAudioSource.h" #include "sources/juce_ReverbAudioSource.h" #include "sources/juce_ToneGeneratorAudioSource.h" #include "synthesisers/juce_Synthesiser.h" } #endif // JUCE_AUDIO_BASICS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm000066400000000000000000000017051320201440200312300ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_audio_basics.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/juce_module_info000066400000000000000000000015421320201440200304720ustar00rootroot00000000000000{ "id": "juce_audio_basics", "name": "JUCE audio and midi data classes", "version": "3.2.0", "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", "dependencies": [ { "id": "juce_core", "version": "matching" } ], "include": "juce_audio_basics.h", "compile": [ { "file": "juce_audio_basics.cpp", "target": "! xcode" }, { "file": "juce_audio_basics.mm", "target": "xcode" } ], "browse": [ "buffers/*", "midi/*", "effects/*", "sources/*", "synthesisers/*" ], "OSXFrameworks": "Accelerate", "iOSFrameworks": "Accelerate" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/000077500000000000000000000000001320201440200261615ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp000066400000000000000000000165611320201440200317200ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace MidiBufferHelpers { inline int getEventTime (const void* const d) noexcept { return readUnaligned (d); } inline uint16 getEventDataSize (const void* const d) noexcept { return readUnaligned (static_cast (d) + sizeof (int32)); } inline uint16 getEventTotalSize (const void* const d) noexcept { return getEventDataSize (d) + sizeof (int32) + sizeof (uint16); } static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept { unsigned int byte = (unsigned int) *data; int size = 0; if (byte == 0xf0 || byte == 0xf7) { const uint8* d = data + 1; while (d < data + maxBytes) if (*d++ == 0xf7) break; size = (int) (d - data); } else if (byte == 0xff) { int n; const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n); size = jmin (maxBytes, n + 2 + bytesLeft); } else if (byte >= 0x80) { size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); } return size; } static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept { while (d < endData && getEventTime (d) <= samplePosition) d += getEventTotalSize (d); return d; } } //============================================================================== MidiBuffer::MidiBuffer() noexcept {} MidiBuffer::~MidiBuffer() {} MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {} MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept { data = other.data; return *this; } MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept { addEvent (message, 0); } void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); } void MidiBuffer::clear() noexcept { data.clearQuick(); } void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); } bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; } void MidiBuffer::clear (const int startSample, const int numSamples) { uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); data.removeRange ((int) (start - data.begin()), (int) (end - data.begin())); } void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) { addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); } void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber) { const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); if (numBytes > 0) { const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); data.insertMultiple (offset, 0, (int) newItemSize); uint8* const d = data.begin() + offset; writeUnaligned (d, sampleNumber); writeUnaligned (d + 4, static_cast (numBytes)); memcpy (d + 6, newData, (size_t) numBytes); } } void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, const int startSample, const int numSamples, const int sampleDeltaToAdd) { Iterator i (otherBuffer); i.setNextSamplePosition (startSample); const uint8* eventData; int eventSize, position; while (i.getNextEvent (eventData, eventSize, position) && (position < startSample + numSamples || numSamples < 0)) { addEvent (eventData, eventSize, position + sampleDeltaToAdd); } } int MidiBuffer::getNumEvents() const noexcept { int n = 0; const uint8* const end = data.end(); for (const uint8* d = data.begin(); d < end; ++n) d += MidiBufferHelpers::getEventTotalSize (d); return n; } int MidiBuffer::getFirstEventTime() const noexcept { return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0; } int MidiBuffer::getLastEventTime() const noexcept { if (data.size() == 0) return 0; const uint8* const endData = data.end(); for (const uint8* d = data.begin();;) { const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d); if (nextOne >= endData) return MidiBufferHelpers::getEventTime (d); d = nextOne; } } //============================================================================== MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept : buffer (b), data (b.data.begin()) { } MidiBuffer::Iterator::~Iterator() noexcept { } void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept { data = buffer.data.begin(); const uint8* const dataEnd = buffer.data.end(); while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition) data += MidiBufferHelpers::getEventTotalSize (data); } bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept { if (data >= buffer.data.end()) return false; samplePosition = MidiBufferHelpers::getEventTime (data); const int itemSize = MidiBufferHelpers::getEventDataSize (data); numBytes = itemSize; midiData = data + sizeof (int32) + sizeof (uint16); data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; return true; } bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept { if (data >= buffer.data.end()) return false; samplePosition = MidiBufferHelpers::getEventTime (data); const int itemSize = MidiBufferHelpers::getEventDataSize (data); result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition); data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; return true; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h000066400000000000000000000232551320201440200313630ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIBUFFER_H_INCLUDED #define JUCE_MIDIBUFFER_H_INCLUDED //============================================================================== /** Holds a sequence of time-stamped midi events. Analogous to the AudioSampleBuffer, this holds a set of midi events with integer time-stamps. The buffer is kept sorted in order of the time-stamps. If you're working with a sequence of midi events that may need to be manipulated or read/written to a midi file, then MidiMessageSequence is probably a more appropriate container. MidiBuffer is designed for lower-level streams of raw midi data. @see MidiMessage */ class JUCE_API MidiBuffer { public: //============================================================================== /** Creates an empty MidiBuffer. */ MidiBuffer() noexcept; /** Creates a MidiBuffer containing a single midi message. */ explicit MidiBuffer (const MidiMessage& message) noexcept; /** Creates a copy of another MidiBuffer. */ MidiBuffer (const MidiBuffer&) noexcept; /** Makes a copy of another MidiBuffer. */ MidiBuffer& operator= (const MidiBuffer&) noexcept; /** Destructor */ ~MidiBuffer(); //============================================================================== /** Removes all events from the buffer. */ void clear() noexcept; /** Removes all events between two times from the buffer. All events for which (start <= event position < start + numSamples) will be removed. */ void clear (int start, int numSamples); /** Returns true if the buffer is empty. To actually retrieve the events, use a MidiBuffer::Iterator object */ bool isEmpty() const noexcept; /** Counts the number of events in the buffer. This is actually quite a slow operation, as it has to iterate through all the events, so you might prefer to call isEmpty() if that's all you need to know. */ int getNumEvents() const noexcept; /** Adds an event to the buffer. The sample number will be used to determine the position of the event in the buffer, which is always kept sorted. The MidiMessage's timestamp is ignored. If an event is added whose sample position is the same as one or more events already in the buffer, the new event will be placed after the existing ones. To retrieve events, use a MidiBuffer::Iterator object */ void addEvent (const MidiMessage& midiMessage, int sampleNumber); /** Adds an event to the buffer from raw midi data. The sample number will be used to determine the position of the event in the buffer, which is always kept sorted. If an event is added whose sample position is the same as one or more events already in the buffer, the new event will be placed after the existing ones. The event data will be inspected to calculate the number of bytes in length that the midi event really takes up, so maxBytesOfMidiData may be longer than the data that actually gets stored. E.g. if you pass in a note-on and a length of 4 bytes, it'll actually only store 3 bytes. If the midi data is invalid, it might not add an event at all. To retrieve events, use a MidiBuffer::Iterator object */ void addEvent (const void* rawMidiData, int maxBytesOfMidiData, int sampleNumber); /** Adds some events from another buffer to this one. @param otherBuffer the buffer containing the events you want to add @param startSample the lowest sample number in the source buffer for which events should be added. Any source events whose timestamp is less than this will be ignored @param numSamples the valid range of samples from the source buffer for which events should be added - i.e. events in the source buffer whose timestamp is greater than or equal to (startSample + numSamples) will be ignored. If this value is less than 0, all events after startSample will be taken. @param sampleDeltaToAdd a value which will be added to the source timestamps of the events that are added to this buffer */ void addEvents (const MidiBuffer& otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd); /** Returns the sample number of the first event in the buffer. If the buffer's empty, this will just return 0. */ int getFirstEventTime() const noexcept; /** Returns the sample number of the last event in the buffer. If the buffer's empty, this will just return 0. */ int getLastEventTime() const noexcept; //============================================================================== /** Exchanges the contents of this buffer with another one. This is a quick operation, because no memory allocating or copying is done, it just swaps the internal state of the two buffers. */ void swapWith (MidiBuffer&) noexcept; /** Preallocates some memory for the buffer to use. This helps to avoid needing to reallocate space when the buffer has messages added to it. */ void ensureSize (size_t minimumNumBytes); //============================================================================== /** Used to iterate through the events in a MidiBuffer. Note that altering the buffer while an iterator is using it isn't a safe operation. @see MidiBuffer */ class JUCE_API Iterator { public: //============================================================================== /** Creates an Iterator for this MidiBuffer. */ Iterator (const MidiBuffer&) noexcept; /** Destructor. */ ~Iterator() noexcept; //============================================================================== /** Repositions the iterator so that the next event retrieved will be the first one whose sample position is at greater than or equal to the given position. */ void setNextSamplePosition (int samplePosition) noexcept; /** Retrieves a copy of the next event from the buffer. @param result on return, this will be the message. The MidiMessage's timestamp is set to the same value as samplePosition. @param samplePosition on return, this will be the position of the event, as a sample index in the buffer @returns true if an event was found, or false if the iterator has reached the end of the buffer */ bool getNextEvent (MidiMessage& result, int& samplePosition) noexcept; /** Retrieves the next event from the buffer. @param midiData on return, this pointer will be set to a block of data containing the midi message. Note that to make it fast, this is a pointer directly into the MidiBuffer's internal data, so is only valid temporarily until the MidiBuffer is altered. @param numBytesOfMidiData on return, this is the number of bytes of data used by the midi message @param samplePosition on return, this will be the position of the event, as a sample index in the buffer @returns true if an event was found, or false if the iterator has reached the end of the buffer */ bool getNextEvent (const uint8* &midiData, int& numBytesOfMidiData, int& samplePosition) noexcept; private: //============================================================================== const MidiBuffer& buffer; const uint8* data; JUCE_DECLARE_NON_COPYABLE (Iterator) }; /** The raw data holding this buffer. Obviously access to this data is provided at your own risk. Its internal format could change in future, so don't write code that relies on it! */ Array data; private: JUCE_LEAK_DETECTOR (MidiBuffer) }; #endif // JUCE_MIDIBUFFER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp000066400000000000000000000314201320201440200313550ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace MidiFileHelpers { static void writeVariableLengthInt (OutputStream& out, unsigned int v) { unsigned int buffer = v & 0x7f; while ((v >>= 7) != 0) { buffer <<= 8; buffer |= ((v & 0x7f) | 0x80); } for (;;) { out.writeByte ((char) buffer); if (buffer & 0x80) buffer >>= 8; else break; } } static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) noexcept { unsigned int ch = ByteOrder::bigEndianInt (data); data += 4; if (ch != ByteOrder::bigEndianInt ("MThd")) { bool ok = false; if (ch == ByteOrder::bigEndianInt ("RIFF")) { for (int i = 0; i < 8; ++i) { ch = ByteOrder::bigEndianInt (data); data += 4; if (ch == ByteOrder::bigEndianInt ("MThd")) { ok = true; break; } } } if (! ok) return false; } unsigned int bytesRemaining = ByteOrder::bigEndianInt (data); data += 4; fileType = (short) ByteOrder::bigEndianShort (data); data += 2; numberOfTracks = (short) ByteOrder::bigEndianShort (data); data += 2; timeFormat = (short) ByteOrder::bigEndianShort (data); data += 2; bytesRemaining -= 6; data += bytesRemaining; return true; } static double convertTicksToSeconds (const double time, const MidiMessageSequence& tempoEvents, const int timeFormat) { if (timeFormat < 0) return time / (-(timeFormat >> 8) * (timeFormat & 0xff)); double lastTime = 0.0, correctedTime = 0.0; const double tickLen = 1.0 / (timeFormat & 0x7fff); double secsPerTick = 0.5 * tickLen; const int numEvents = tempoEvents.getNumEvents(); for (int i = 0; i < numEvents; ++i) { const MidiMessage& m = tempoEvents.getEventPointer(i)->message; const double eventTime = m.getTimeStamp(); if (eventTime >= time) break; correctedTime += (eventTime - lastTime) * secsPerTick; lastTime = eventTime; if (m.isTempoMetaEvent()) secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote(); while (i + 1 < numEvents) { const MidiMessage& m2 = tempoEvents.getEventPointer(i + 1)->message; if (m2.getTimeStamp() != eventTime) break; if (m2.isTempoMetaEvent()) secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote(); ++i; } } return correctedTime + (time - lastTime) * secsPerTick; } // a comparator that puts all the note-offs before note-ons that have the same time struct Sorter { static int compareElements (const MidiMessageSequence::MidiEventHolder* const first, const MidiMessageSequence::MidiEventHolder* const second) noexcept { const double diff = (first->message.getTimeStamp() - second->message.getTimeStamp()); if (diff > 0) return 1; if (diff < 0) return -1; if (first->message.isNoteOff() && second->message.isNoteOn()) return -1; if (first->message.isNoteOn() && second->message.isNoteOff()) return 1; return 0; } }; template static void findAllMatchingEvents (const OwnedArray& tracks, MidiMessageSequence& results, MethodType method) { for (int i = 0; i < tracks.size(); ++i) { const MidiMessageSequence& track = *tracks.getUnchecked(i); const int numEvents = track.getNumEvents(); for (int j = 0; j < numEvents; ++j) { const MidiMessage& m = track.getEventPointer(j)->message; if ((m.*method)()) results.addEvent (m); } } } } //============================================================================== MidiFile::MidiFile() : timeFormat ((short) (unsigned short) 0xe728) { } MidiFile::~MidiFile() { } void MidiFile::clear() { tracks.clear(); } //============================================================================== int MidiFile::getNumTracks() const noexcept { return tracks.size(); } const MidiMessageSequence* MidiFile::getTrack (const int index) const noexcept { return tracks [index]; } void MidiFile::addTrack (const MidiMessageSequence& trackSequence) { tracks.add (new MidiMessageSequence (trackSequence)); } //============================================================================== short MidiFile::getTimeFormat() const noexcept { return timeFormat; } void MidiFile::setTicksPerQuarterNote (const int ticks) noexcept { timeFormat = (short) ticks; } void MidiFile::setSmpteTimeFormat (const int framesPerSecond, const int subframeResolution) noexcept { timeFormat = (short) (((-framesPerSecond) << 8) | subframeResolution); } //============================================================================== void MidiFile::findAllTempoEvents (MidiMessageSequence& results) const { MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTempoMetaEvent); } void MidiFile::findAllTimeSigEvents (MidiMessageSequence& results) const { MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTimeSignatureMetaEvent); } void MidiFile::findAllKeySigEvents (MidiMessageSequence& results) const { MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isKeySignatureMetaEvent); } double MidiFile::getLastTimestamp() const { double t = 0.0; for (int i = tracks.size(); --i >= 0;) t = jmax (t, tracks.getUnchecked(i)->getEndTime()); return t; } //============================================================================== bool MidiFile::readFrom (InputStream& sourceStream) { clear(); MemoryBlock data; const int maxSensibleMidiFileSize = 2 * 1024 * 1024; // (put a sanity-check on the file size, as midi files are generally small) if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize)) { size_t size = data.getSize(); const uint8* d = static_cast (data.getData()); short fileType, expectedTracks; if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks)) { size -= (size_t) (d - static_cast (data.getData())); int track = 0; while (size > 0 && track < expectedTracks) { const int chunkType = (int) ByteOrder::bigEndianInt (d); d += 4; const int chunkSize = (int) ByteOrder::bigEndianInt (d); d += 4; if (chunkSize <= 0) break; if (chunkType == (int) ByteOrder::bigEndianInt ("MTrk")) readNextTrack (d, chunkSize); size -= (size_t) chunkSize + 8; d += chunkSize; ++track; } return true; } } return false; } void MidiFile::readNextTrack (const uint8* data, int size) { double time = 0; uint8 lastStatusByte = 0; MidiMessageSequence result; while (size > 0) { int bytesUsed; const int delay = MidiMessage::readVariableLengthVal (data, bytesUsed); data += bytesUsed; size -= bytesUsed; time += delay; int messSize = 0; const MidiMessage mm (data, size, messSize, lastStatusByte, time); if (messSize <= 0) break; size -= messSize; data += messSize; result.addEvent (mm); const uint8 firstByte = *(mm.getRawData()); if ((firstByte & 0xf0) != 0xf0) lastStatusByte = firstByte; } // use a sort that puts all the note-offs before note-ons that have the same time MidiFileHelpers::Sorter sorter; result.list.sort (sorter, true); addTrack (result); tracks.getLast()->updateMatchedPairs(); } //============================================================================== void MidiFile::convertTimestampTicksToSeconds() { MidiMessageSequence tempoEvents; findAllTempoEvents (tempoEvents); findAllTimeSigEvents (tempoEvents); if (timeFormat != 0) { for (int i = 0; i < tracks.size(); ++i) { const MidiMessageSequence& ms = *tracks.getUnchecked(i); for (int j = ms.getNumEvents(); --j >= 0;) { MidiMessage& m = ms.getEventPointer(j)->message; m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat)); } } } } //============================================================================== bool MidiFile::writeTo (OutputStream& out, int midiFileType) { jassert (midiFileType >= 0 && midiFileType <= 2); out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd")); out.writeIntBigEndian (6); out.writeShortBigEndian ((short) midiFileType); out.writeShortBigEndian ((short) tracks.size()); out.writeShortBigEndian (timeFormat); for (int i = 0; i < tracks.size(); ++i) writeTrack (out, i); out.flush(); return true; } void MidiFile::writeTrack (OutputStream& mainOut, const int trackNum) { MemoryOutputStream out; const MidiMessageSequence& ms = *tracks.getUnchecked (trackNum); int lastTick = 0; uint8 lastStatusByte = 0; bool endOfTrackEventWritten = false; for (int i = 0; i < ms.getNumEvents(); ++i) { const MidiMessage& mm = ms.getEventPointer(i)->message; if (mm.isEndOfTrackMetaEvent()) endOfTrackEventWritten = true; const int tick = roundToInt (mm.getTimeStamp()); const int delta = jmax (0, tick - lastTick); MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta); lastTick = tick; const uint8* data = mm.getRawData(); int dataSize = mm.getRawDataSize(); const uint8 statusByte = data[0]; if (statusByte == lastStatusByte && (statusByte & 0xf0) != 0xf0 && dataSize > 1 && i > 0) { ++data; --dataSize; } else if (statusByte == 0xf0) // Write sysex message with length bytes. { out.writeByte ((char) statusByte); ++data; --dataSize; MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize); } out.write (data, (size_t) dataSize); lastStatusByte = statusByte; } if (! endOfTrackEventWritten) { out.writeByte (0); // (tick delta) const MidiMessage m (MidiMessage::endOfTrack()); out.write (m.getRawData(), (size_t) m.getRawDataSize()); } mainOut.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MTrk")); mainOut.writeIntBigEndian ((int) out.getDataSize()); mainOut << out; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h000066400000000000000000000161201320201440200310220ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIFILE_H_INCLUDED #define JUCE_MIDIFILE_H_INCLUDED //============================================================================== /** Reads/writes standard midi format files. To read a midi file, create a MidiFile object and call its readFrom() method. You can then get the individual midi tracks from it using the getTrack() method. To write a file, create a MidiFile object, add some MidiMessageSequence objects to it using the addTrack() method, and then call its writeTo() method to stream it out. @see MidiMessageSequence */ class JUCE_API MidiFile { public: //============================================================================== /** Creates an empty MidiFile object. */ MidiFile(); /** Destructor. */ ~MidiFile(); //============================================================================== /** Returns the number of tracks in the file. @see getTrack, addTrack */ int getNumTracks() const noexcept; /** Returns a pointer to one of the tracks in the file. @returns a pointer to the track, or nullptr if the index is out-of-range @see getNumTracks, addTrack */ const MidiMessageSequence* getTrack (int index) const noexcept; /** Adds a midi track to the file. This will make its own internal copy of the sequence that is passed-in. @see getNumTracks, getTrack */ void addTrack (const MidiMessageSequence& trackSequence); /** Removes all midi tracks from the file. @see getNumTracks */ void clear(); /** Returns the raw time format code that will be written to a stream. After reading a midi file, this method will return the time-format that was read from the file's header. It can be changed using the setTicksPerQuarterNote() or setSmpteTimeFormat() methods. If the value returned is positive, it indicates the number of midi ticks per quarter-note - see setTicksPerQuarterNote(). It it's negative, the upper byte indicates the frames-per-second (but negative), and the lower byte is the number of ticks per frame - see setSmpteTimeFormat(). */ short getTimeFormat() const noexcept; /** Sets the time format to use when this file is written to a stream. If this is called, the file will be written as bars/beats using the specified resolution, rather than SMPTE absolute times, as would be used if setSmpteTimeFormat() had been called instead. @param ticksPerQuarterNote e.g. 96, 960 @see setSmpteTimeFormat */ void setTicksPerQuarterNote (int ticksPerQuarterNote) noexcept; /** Sets the time format to use when this file is written to a stream. If this is called, the file will be written using absolute times, rather than bars/beats as would be the case if setTicksPerBeat() had been called instead. @param framesPerSecond must be 24, 25, 29 or 30 @param subframeResolution the sub-second resolution, e.g. 4 (midi time code), 8, 10, 80 (SMPTE bit resolution), or 100. For millisecond timing, setSmpteTimeFormat (25, 40) @see setTicksPerBeat */ void setSmpteTimeFormat (int framesPerSecond, int subframeResolution) noexcept; //============================================================================== /** Makes a list of all the tempo-change meta-events from all tracks in the midi file. Useful for finding the positions of all the tempo changes in a file. @param tempoChangeEvents a list to which all the events will be added */ void findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const; /** Makes a list of all the time-signature meta-events from all tracks in the midi file. Useful for finding the positions of all the tempo changes in a file. @param timeSigEvents a list to which all the events will be added */ void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const; /** Makes a list of all the time-signature meta-events from all tracks in the midi file. @param keySigEvents a list to which all the events will be added */ void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const; /** Returns the latest timestamp in any of the tracks. (Useful for finding the length of the file). */ double getLastTimestamp() const; //============================================================================== /** Reads a midi file format stream. After calling this, you can get the tracks that were read from the file by using the getNumTracks() and getTrack() methods. The timestamps of the midi events in the tracks will represent their positions in terms of midi ticks. To convert them to seconds, use the convertTimestampTicksToSeconds() method. @returns true if the stream was read successfully */ bool readFrom (InputStream& sourceStream); /** Writes the midi tracks as a standard midi file. The midiFileType value is written as the file's format type, which can be 0, 1 or 2 - see the midi file spec for more info about that. @returns true if the operation succeeded. */ bool writeTo (OutputStream& destStream, int midiFileType = 1); /** Converts the timestamp of all the midi events from midi ticks to seconds. This will use the midi time format and tempo/time signature info in the tracks to convert all the timestamps to absolute values in seconds. */ void convertTimestampTicksToSeconds(); private: //============================================================================== OwnedArray tracks; short timeFormat; void readNextTrack (const uint8*, int size); void writeTrack (OutputStream&, int trackNum); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiFile) }; #endif // JUCE_MIDIFILE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp000066400000000000000000000134001320201440200332350ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ MidiKeyboardState::MidiKeyboardState() { zerostruct (noteStates); } MidiKeyboardState::~MidiKeyboardState() { } //============================================================================== void MidiKeyboardState::reset() { const ScopedLock sl (lock); zerostruct (noteStates); eventsToAdd.clear(); } bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept { jassert (midiChannel >= 0 && midiChannel <= 16); return isPositiveAndBelow (n, (int) 128) && (noteStates[n] & (1 << (midiChannel - 1))) != 0; } bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const int n) const noexcept { return isPositiveAndBelow (n, (int) 128) && (noteStates[n] & midiChannelMask) != 0; } void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity) { jassert (midiChannel >= 0 && midiChannel <= 16); jassert (isPositiveAndBelow (midiNoteNumber, (int) 128)); const ScopedLock sl (lock); if (isPositiveAndBelow (midiNoteNumber, (int) 128)) { const int timeNow = (int) Time::getMillisecondCounter(); eventsToAdd.addEvent (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity), timeNow); eventsToAdd.clear (0, timeNow - 500); noteOnInternal (midiChannel, midiNoteNumber, velocity); } } void MidiKeyboardState::noteOnInternal (const int midiChannel, const int midiNoteNumber, const float velocity) { if (isPositiveAndBelow (midiNoteNumber, (int) 128)) { noteStates [midiNoteNumber] |= (1 << (midiChannel - 1)); for (int i = listeners.size(); --i >= 0;) listeners.getUnchecked(i)->handleNoteOn (this, midiChannel, midiNoteNumber, velocity); } } void MidiKeyboardState::noteOff (const int midiChannel, const int midiNoteNumber) { const ScopedLock sl (lock); if (isNoteOn (midiChannel, midiNoteNumber)) { const int timeNow = (int) Time::getMillisecondCounter(); eventsToAdd.addEvent (MidiMessage::noteOff (midiChannel, midiNoteNumber), timeNow); eventsToAdd.clear (0, timeNow - 500); noteOffInternal (midiChannel, midiNoteNumber); } } void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiNoteNumber) { if (isNoteOn (midiChannel, midiNoteNumber)) { noteStates [midiNoteNumber] &= ~(1 << (midiChannel - 1)); for (int i = listeners.size(); --i >= 0;) listeners.getUnchecked(i)->handleNoteOff (this, midiChannel, midiNoteNumber); } } void MidiKeyboardState::allNotesOff (const int midiChannel) { const ScopedLock sl (lock); if (midiChannel <= 0) { for (int i = 1; i <= 16; ++i) allNotesOff (i); } else { for (int i = 0; i < 128; ++i) noteOff (midiChannel, i); } } void MidiKeyboardState::processNextMidiEvent (const MidiMessage& message) { if (message.isNoteOn()) { noteOnInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity()); } else if (message.isNoteOff()) { noteOffInternal (message.getChannel(), message.getNoteNumber()); } else if (message.isAllNotesOff()) { for (int i = 0; i < 128; ++i) noteOffInternal (message.getChannel(), i); } } void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, const int startSample, const int numSamples, const bool injectIndirectEvents) { MidiBuffer::Iterator i (buffer); MidiMessage message; int time; const ScopedLock sl (lock); while (i.getNextEvent (message, time)) processNextMidiEvent (message); if (injectIndirectEvents) { MidiBuffer::Iterator i2 (eventsToAdd); const int firstEventToAdd = eventsToAdd.getFirstEventTime(); const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd); while (i2.getNextEvent (message, time)) { const int pos = jlimit (0, numSamples - 1, roundToInt ((time - firstEventToAdd) * scaleFactor)); buffer.addEvent (message, startSample + pos); } } eventsToAdd.clear(); } //============================================================================== void MidiKeyboardState::addListener (MidiKeyboardStateListener* const listener) { const ScopedLock sl (lock); listeners.addIfNotAlreadyThere (listener); } void MidiKeyboardState::removeListener (MidiKeyboardStateListener* const listener) { const ScopedLock sl (lock); listeners.removeFirstMatchingValue (listener); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h000066400000000000000000000203211320201440200327020ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIKEYBOARDSTATE_H_INCLUDED #define JUCE_MIDIKEYBOARDSTATE_H_INCLUDED class MidiKeyboardState; //============================================================================== /** Receives events from a MidiKeyboardState object. @see MidiKeyboardState */ class JUCE_API MidiKeyboardStateListener { public: //============================================================================== MidiKeyboardStateListener() noexcept {} virtual ~MidiKeyboardStateListener() {} //============================================================================== /** Called when one of the MidiKeyboardState's keys is pressed. This will be called synchronously when the state is either processing a buffer in its MidiKeyboardState::processNextMidiBuffer() method, or when a note is being played with its MidiKeyboardState::noteOn() method. Note that this callback could happen from an audio callback thread, so be careful not to block, and avoid any UI activity in the callback. */ virtual void handleNoteOn (MidiKeyboardState* source, int midiChannel, int midiNoteNumber, float velocity) = 0; /** Called when one of the MidiKeyboardState's keys is released. This will be called synchronously when the state is either processing a buffer in its MidiKeyboardState::processNextMidiBuffer() method, or when a note is being played with its MidiKeyboardState::noteOff() method. Note that this callback could happen from an audio callback thread, so be careful not to block, and avoid any UI activity in the callback. */ virtual void handleNoteOff (MidiKeyboardState* source, int midiChannel, int midiNoteNumber) = 0; }; //============================================================================== /** Represents a piano keyboard, keeping track of which keys are currently pressed. This object can parse a stream of midi events, using them to update its idea of which keys are pressed for each individiual midi channel. When keys go up or down, it can broadcast these events to listener objects. It also allows key up/down events to be triggered with its noteOn() and noteOff() methods, and midi messages for these events will be merged into the midi stream that gets processed by processNextMidiBuffer(). */ class JUCE_API MidiKeyboardState { public: //============================================================================== MidiKeyboardState(); ~MidiKeyboardState(); //============================================================================== /** Resets the state of the object. All internal data for all the channels is reset, but no events are sent as a result. If you want to release any keys that are currently down, and to send out note-up midi messages for this, use the allNotesOff() method instead. */ void reset(); /** Returns true if the given midi key is currently held down for the given midi channel. The channel number must be between 1 and 16. If you want to see if any notes are on for a range of channels, use the isNoteOnForChannels() method. */ bool isNoteOn (int midiChannel, int midiNoteNumber) const noexcept; /** Returns true if the given midi key is currently held down on any of a set of midi channels. The channel mask has a bit set for each midi channel you want to test for - bit 0 = midi channel 1, bit 1 = midi channel 2, etc. If a note is on for at least one of the specified channels, this returns true. */ bool isNoteOnForChannels (int midiChannelMask, int midiNoteNumber) const noexcept; /** Turns a specified note on. This will cause a suitable midi note-on event to be injected into the midi buffer during the next call to processNextMidiBuffer(). It will also trigger a synchronous callback to the listeners to tell them that the key has gone down. */ void noteOn (int midiChannel, int midiNoteNumber, float velocity); /** Turns a specified note off. This will cause a suitable midi note-off event to be injected into the midi buffer during the next call to processNextMidiBuffer(). It will also trigger a synchronous callback to the listeners to tell them that the key has gone up. But if the note isn't acutally down for the given channel, this method will in fact do nothing. */ void noteOff (int midiChannel, int midiNoteNumber); /** This will turn off any currently-down notes for the given midi channel. If you pass 0 for the midi channel, it will in fact turn off all notes on all channels. Calling this method will make calls to noteOff(), so can trigger synchronous callbacks and events being added to the midi stream. */ void allNotesOff (int midiChannel); //============================================================================== /** Looks at a key-up/down event and uses it to update the state of this object. To process a buffer full of midi messages, use the processNextMidiBuffer() method instead. */ void processNextMidiEvent (const MidiMessage& message); /** Scans a midi stream for up/down events and adds its own events to it. This will look for any up/down events and use them to update the internal state, synchronously making suitable callbacks to the listeners. If injectIndirectEvents is true, then midi events to produce the recent noteOn() and noteOff() calls will be added into the buffer. Only the section of the buffer whose timestamps are between startSample and (startSample + numSamples) will be affected, and any events added will be placed between these times. If you're going to use this method, you'll need to keep calling it regularly for it to work satisfactorily. To process a single midi event at a time, use the processNextMidiEvent() method instead. */ void processNextMidiBuffer (MidiBuffer& buffer, int startSample, int numSamples, bool injectIndirectEvents); //============================================================================== /** Registers a listener for callbacks when keys go up or down. @see removeListener */ void addListener (MidiKeyboardStateListener* listener); /** Deregisters a listener. @see addListener */ void removeListener (MidiKeyboardStateListener* listener); private: //============================================================================== CriticalSection lock; uint16 noteStates [128]; MidiBuffer eventsToAdd; Array listeners; void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity); void noteOffInternal (int midiChannel, int midiNoteNumber); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState) }; #endif // JUCE_MIDIKEYBOARDSTATE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp000066400000000000000000001211611320201440200320640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace MidiHelpers { inline uint8 initialByte (const int type, const int channel) noexcept { return (uint8) (type | jlimit (0, 15, channel - 1)); } inline uint8 validVelocity (const int v) noexcept { return (uint8) jlimit (0, 127, v); } } //============================================================================== int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept { numBytesUsed = 0; int v = 0, i; do { i = (int) *data++; if (++numBytesUsed > 6) break; v = (v << 7) + (i & 0x7f); } while (i & 0x80); return v; } int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept { // this method only works for valid starting bytes of a short midi message jassert (firstByte >= 0x80 && firstByte != 0xf0 && firstByte != 0xf7); static const char messageLengths[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; return messageLengths [firstByte & 0x7f]; } //============================================================================== MidiMessage::MidiMessage() noexcept : timeStamp (0), size (2) { preallocatedData.asBytes[0] = 0xf0; preallocatedData.asBytes[1] = 0xf7; } MidiMessage::MidiMessage (const void* const d, const int dataSize, const double t) : timeStamp (t), size (dataSize) { jassert (dataSize > 0); memcpy (allocateSpace (dataSize), d, (size_t) dataSize); // check that the length matches the data.. jassert (size > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size); } MidiMessage::MidiMessage (const int byte1, const double t) noexcept : timeStamp (t), size (1) { preallocatedData.asBytes[0] = (uint8) byte1; // check that the length matches the data.. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1); } MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept : timeStamp (t), size (2) { preallocatedData.asBytes[0] = (uint8) byte1; preallocatedData.asBytes[1] = (uint8) byte2; // check that the length matches the data.. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2); } MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept : timeStamp (t), size (3) { preallocatedData.asBytes[0] = (uint8) byte1; preallocatedData.asBytes[1] = (uint8) byte2; preallocatedData.asBytes[2] = (uint8) byte3; // check that the length matches the data.. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3); } MidiMessage::MidiMessage (const MidiMessage& other) : timeStamp (other.timeStamp), size (other.size) { if (other.allocatedData != nullptr) { allocatedData.malloc ((size_t) size); memcpy (allocatedData, other.allocatedData, (size_t) size); } else { preallocatedData.asInt32 = other.preallocatedData.asInt32; } } MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) : timeStamp (newTimeStamp), size (other.size) { if (other.allocatedData != nullptr) { allocatedData.malloc ((size_t) size); memcpy (allocatedData, other.allocatedData, (size_t) size); } else { preallocatedData.asInt32 = other.preallocatedData.asInt32; } } MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t, bool sysexHasEmbeddedLength) : timeStamp (t) { const uint8* src = static_cast (srcData); unsigned int byte = (unsigned int) *src; if (byte < 0x80) { byte = (unsigned int) (uint8) lastStatusByte; numBytesUsed = -1; } else { numBytesUsed = 0; --sz; ++src; } if (byte >= 0x80) { if (byte == 0xf0) { const uint8* d = src; bool haveReadAllLengthBytes = ! sysexHasEmbeddedLength; int numVariableLengthSysexBytes = 0; while (d < src + sz) { if (*d >= 0x80) { if (*d == 0xf7) { ++d; // include the trailing 0xf7 when we hit it break; } if (haveReadAllLengthBytes) // if we see a 0x80 bit set after the initial data length break; // bytes, assume it's the end of the sysex ++numVariableLengthSysexBytes; } else if (! haveReadAllLengthBytes) { haveReadAllLengthBytes = true; ++numVariableLengthSysexBytes; } ++d; } src += numVariableLengthSysexBytes; size = 1 + (int) (d - src); uint8* dest = allocateSpace (size); *dest = (uint8) byte; memcpy (dest + 1, src, (size_t) (size - 1)); numBytesUsed += numVariableLengthSysexBytes; // (these aren't counted in the size) } else if (byte == 0xff) { int n; const int bytesLeft = readVariableLengthVal (src + 1, n); size = jmin (sz + 1, n + 2 + bytesLeft); uint8* dest = allocateSpace (size); *dest = (uint8) byte; memcpy (dest + 1, src, (size_t) size - 1); } else { preallocatedData.asInt32 = 0; size = getMessageLengthFromFirstByte ((uint8) byte); preallocatedData.asBytes[0] = (uint8) byte; if (size > 1) { preallocatedData.asBytes[1] = src[0]; if (size > 2) preallocatedData.asBytes[2] = src[1]; } } numBytesUsed += size; } else { preallocatedData.asInt32 = 0; size = 0; } } MidiMessage& MidiMessage::operator= (const MidiMessage& other) { if (this != &other) { timeStamp = other.timeStamp; size = other.size; if (other.allocatedData != nullptr) { allocatedData.malloc ((size_t) size); memcpy (allocatedData, other.allocatedData, (size_t) size); } else { allocatedData.free(); preallocatedData.asInt32 = other.preallocatedData.asInt32; } } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MidiMessage::MidiMessage (MidiMessage&& other) noexcept : timeStamp (other.timeStamp), size (other.size) { if (other.allocatedData != nullptr) allocatedData.swapWith (other.allocatedData); else preallocatedData.asInt32 = other.preallocatedData.asInt32; } MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept { jassert (this != &other); // shouldn't be possible timeStamp = other.timeStamp; size = other.size; allocatedData.swapWith (other.allocatedData); preallocatedData.asInt32 = other.preallocatedData.asInt32; return *this; } #endif MidiMessage::~MidiMessage() {} uint8* MidiMessage::allocateSpace (int bytes) { if (bytes > 4) { allocatedData.malloc ((size_t) bytes); return allocatedData; } return preallocatedData.asBytes; } int MidiMessage::getChannel() const noexcept { const uint8* const data = getRawData(); if ((data[0] & 0xf0) != 0xf0) return (data[0] & 0xf) + 1; return 0; } bool MidiMessage::isForChannel (const int channel) const noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 const uint8* const data = getRawData(); return ((data[0] & 0xf) == channel - 1) && ((data[0] & 0xf0) != 0xf0); } void MidiMessage::setChannel (const int channel) noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 uint8* const data = getData(); if ((data[0] & 0xf0) != (uint8) 0xf0) data[0] = (uint8) ((data[0] & (uint8) 0xf0) | (uint8)(channel - 1)); } bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept { const uint8* const data = getRawData(); return ((data[0] & 0xf0) == 0x90) && (returnTrueForVelocity0 || data[2] != 0); } bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept { const uint8* const data = getRawData(); return ((data[0] & 0xf0) == 0x80) || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90)); } bool MidiMessage::isNoteOnOrOff() const noexcept { const uint8* const data = getRawData(); const int d = data[0] & 0xf0; return (d == 0x90) || (d == 0x80); } int MidiMessage::getNoteNumber() const noexcept { return getRawData()[1]; } void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept { if (isNoteOnOrOff()) getData()[1] = (uint8) (newNoteNumber & 127); } uint8 MidiMessage::getVelocity() const noexcept { if (isNoteOnOrOff()) return getRawData()[2]; return 0; } float MidiMessage::getFloatVelocity() const noexcept { return getVelocity() * (1.0f / 127.0f); } void MidiMessage::setVelocity (const float newVelocity) noexcept { if (isNoteOnOrOff()) getData()[2] = MidiHelpers::validVelocity (roundToInt (newVelocity * 127.0f)); } void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept { if (isNoteOnOrOff()) { uint8* const data = getData(); data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2])); } } bool MidiMessage::isAftertouch() const noexcept { return (getRawData()[0] & 0xf0) == 0xa0; } int MidiMessage::getAfterTouchValue() const noexcept { jassert (isAftertouch()); return getRawData()[2]; } MidiMessage MidiMessage::aftertouchChange (const int channel, const int noteNum, const int aftertouchValue) noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 jassert (isPositiveAndBelow (noteNum, (int) 128)); jassert (isPositiveAndBelow (aftertouchValue, (int) 128)); return MidiMessage (MidiHelpers::initialByte (0xa0, channel), noteNum & 0x7f, aftertouchValue & 0x7f); } bool MidiMessage::isChannelPressure() const noexcept { return (getRawData()[0] & 0xf0) == 0xd0; } int MidiMessage::getChannelPressureValue() const noexcept { jassert (isChannelPressure()); return getRawData()[1]; } MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 jassert (isPositiveAndBelow (pressure, (int) 128)); return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f); } bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && getRawData()[2] >= 64; } bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && getRawData()[2] < 64; } bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && getRawData()[2] >= 64; } bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && getRawData()[2] < 64; } bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && getRawData()[2] >= 64; } bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && getRawData()[2] < 64; } bool MidiMessage::isProgramChange() const noexcept { return (getRawData()[0] & 0xf0) == 0xc0; } int MidiMessage::getProgramChangeNumber() const noexcept { jassert (isProgramChange()); return getRawData()[1]; } MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 return MidiMessage (MidiHelpers::initialByte (0xc0, channel), programNumber & 0x7f); } bool MidiMessage::isPitchWheel() const noexcept { return (getRawData()[0] & 0xf0) == 0xe0; } int MidiMessage::getPitchWheelValue() const noexcept { jassert (isPitchWheel()); const uint8* const data = getRawData(); return data[1] | (data[2] << 7); } MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 jassert (isPositiveAndBelow (position, (int) 0x4000)); return MidiMessage (MidiHelpers::initialByte (0xe0, channel), position & 127, (position >> 7) & 127); } bool MidiMessage::isController() const noexcept { return (getRawData()[0] & 0xf0) == 0xb0; } bool MidiMessage::isControllerOfType (const int controllerType) const noexcept { const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType; } int MidiMessage::getControllerNumber() const noexcept { jassert (isController()); return getRawData()[1]; } int MidiMessage::getControllerValue() const noexcept { jassert (isController()); return getRawData()[2]; } MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept { // the channel must be between 1 and 16 inclusive jassert (channel > 0 && channel <= 16); return MidiMessage (MidiHelpers::initialByte (0xb0, channel), controllerType & 127, value & 127); } MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept { return noteOn (channel, noteNumber, (uint8) (velocity * 127.0f + 0.5f)); } MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const uint8 velocity) noexcept { jassert (channel > 0 && channel <= 16); jassert (isPositiveAndBelow (noteNumber, (int) 128)); return MidiMessage (MidiHelpers::initialByte (0x90, channel), noteNumber & 127, MidiHelpers::validVelocity (velocity)); } MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept { jassert (channel > 0 && channel <= 16); jassert (isPositiveAndBelow (noteNumber, (int) 128)); return MidiMessage (MidiHelpers::initialByte (0x80, channel), noteNumber & 127, MidiHelpers::validVelocity (velocity)); } MidiMessage MidiMessage::allNotesOff (const int channel) noexcept { return controllerEvent (channel, 123, 0); } bool MidiMessage::isAllNotesOff() const noexcept { const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == 123; } MidiMessage MidiMessage::allSoundOff (const int channel) noexcept { return controllerEvent (channel, 120, 0); } bool MidiMessage::isAllSoundOff() const noexcept { const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == 120; } MidiMessage MidiMessage::allControllersOff (const int channel) noexcept { return controllerEvent (channel, 121, 0); } MidiMessage MidiMessage::masterVolume (const float volume) { const int vol = jlimit (0, 0x3fff, roundToInt (volume * 0x4000)); const uint8 buf[] = { 0xf0, 0x7f, 0x7f, 0x04, 0x01, (uint8) (vol & 0x7f), (uint8) (vol >> 7), 0xf7 }; return MidiMessage (buf, 8); } //============================================================================== bool MidiMessage::isSysEx() const noexcept { return *getRawData() == 0xf0; } MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize) { HeapBlock m ((size_t) dataSize + 2); m[0] = 0xf0; memcpy (m + 1, sysexData, (size_t) dataSize); m[dataSize + 1] = 0xf7; return MidiMessage (m, dataSize + 2); } const uint8* MidiMessage::getSysExData() const noexcept { return isSysEx() ? getRawData() + 1 : nullptr; } int MidiMessage::getSysExDataSize() const noexcept { return isSysEx() ? size - 2 : 0; } //============================================================================== bool MidiMessage::isMetaEvent() const noexcept { return *getRawData() == 0xff; } bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0xfe; } int MidiMessage::getMetaEventType() const noexcept { const uint8* const data = getRawData(); return *data != 0xff ? -1 : data[1]; } int MidiMessage::getMetaEventLength() const noexcept { const uint8* const data = getRawData(); if (*data == 0xff) { int n; return jmin (size - 2, readVariableLengthVal (data + 2, n)); } return 0; } const uint8* MidiMessage::getMetaEventData() const noexcept { jassert (isMetaEvent()); int n; const uint8* d = getRawData() + 2; readVariableLengthVal (d, n); return d + n; } bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; } bool MidiMessage::isEndOfTrackMetaEvent() const noexcept { return getMetaEventType() == 47; } bool MidiMessage::isTextMetaEvent() const noexcept { const int t = getMetaEventType(); return t > 0 && t < 16; } String MidiMessage::getTextFromTextMetaEvent() const { const char* const textData = reinterpret_cast (getMetaEventData()); return String (CharPointer_UTF8 (textData), CharPointer_UTF8 (textData + getMetaEventLength())); } MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) { jassert (type > 0 && type < 16); MidiMessage result; const size_t textSize = text.text.sizeInBytes() - 1; uint8 header[8]; size_t n = sizeof (header); header[--n] = (uint8) (textSize & 0x7f); for (size_t i = textSize; (i >>= 7) != 0;) header[--n] = (uint8) ((i & 0x7f) | 0x80); header[--n] = (uint8) type; header[--n] = 0xff; const size_t headerLen = sizeof (header) - n; uint8* const dest = result.allocateSpace ((int) (headerLen + textSize)); result.size = (int) (headerLen + textSize); memcpy (dest, header + n, headerLen); memcpy (dest + headerLen, text.text.getAddress(), textSize); return result; } bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); } bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); } bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } int MidiMessage::getMidiChannelMetaEventChannel() const noexcept { jassert (isMidiChannelMetaEvent()); return getRawData()[3] + 1; } double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept { if (! isTempoMetaEvent()) return 0.0; const uint8* const d = getMetaEventData(); return (((unsigned int) d[0] << 16) | ((unsigned int) d[1] << 8) | d[2]) / 1000000.0; } double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const noexcept { if (timeFormat > 0) { if (! isTempoMetaEvent()) return 0.5 / timeFormat; return getTempoSecondsPerQuarterNote() / timeFormat; } else { const int frameCode = (-timeFormat) >> 8; double framesPerSecond; switch (frameCode) { case 24: framesPerSecond = 24.0; break; case 25: framesPerSecond = 25.0; break; case 29: framesPerSecond = 29.97; break; case 30: framesPerSecond = 30.0; break; default: framesPerSecond = 30.0; break; } return (1.0 / framesPerSecond) / (timeFormat & 0xff); } } MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcept { const uint8 d[] = { 0xff, 81, 3, (uint8) (microsecondsPerQuarterNote >> 16), (uint8) (microsecondsPerQuarterNote >> 8), (uint8) microsecondsPerQuarterNote }; return MidiMessage (d, 6, 0.0); } bool MidiMessage::isTimeSignatureMetaEvent() const noexcept { const uint8* const data = getRawData(); return (data[1] == 0x58) && (*data == (uint8) 0xff); } void MidiMessage::getTimeSignatureInfo (int& numerator, int& denominator) const noexcept { if (isTimeSignatureMetaEvent()) { const uint8* const d = getMetaEventData(); numerator = d[0]; denominator = 1 << d[1]; } else { numerator = 4; denominator = 4; } } MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int denominator) { int n = 1; int powerOfTwo = 0; while (n < denominator) { n <<= 1; ++powerOfTwo; } const uint8 d[] = { 0xff, 0x58, 0x04, (uint8) numerator, (uint8) powerOfTwo, 1, 96 }; return MidiMessage (d, 7, 0.0); } MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept { const uint8 d[] = { 0xff, 0x20, 0x01, (uint8) jlimit (0, 0xff, channel - 1) }; return MidiMessage (d, 4, 0.0); } bool MidiMessage::isKeySignatureMetaEvent() const noexcept { return getMetaEventType() == 0x59; } int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept { return (int) getMetaEventData()[0]; } bool MidiMessage::isKeySignatureMajorKey() const noexcept { return getMetaEventData()[1] == 0; } MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey) { jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7); const uint8 d[] = { 0xff, 0x59, 0x02, (uint8) numberOfSharpsOrFlats, isMinorKey ? (uint8) 1 : (uint8) 0 }; return MidiMessage (d, 5, 0.0); } MidiMessage MidiMessage::endOfTrack() noexcept { return MidiMessage (0xff, 0x2f, 0, 0.0); } //============================================================================== bool MidiMessage::isSongPositionPointer() const noexcept { return *getRawData() == 0xf2; } int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { const uint8* data = getRawData(); return data[1] | (data[2] << 7); } MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept { return MidiMessage (0xf2, positionInMidiBeats & 127, (positionInMidiBeats >> 7) & 127); } bool MidiMessage::isMidiStart() const noexcept { return *getRawData() == 0xfa; } MidiMessage MidiMessage::midiStart() noexcept { return MidiMessage (0xfa); } bool MidiMessage::isMidiContinue() const noexcept { return *getRawData() == 0xfb; } MidiMessage MidiMessage::midiContinue() noexcept { return MidiMessage (0xfb); } bool MidiMessage::isMidiStop() const noexcept { return *getRawData() == 0xfc; } MidiMessage MidiMessage::midiStop() noexcept { return MidiMessage (0xfc); } bool MidiMessage::isMidiClock() const noexcept { return *getRawData() == 0xf8; } MidiMessage MidiMessage::midiClock() noexcept { return MidiMessage (0xf8); } bool MidiMessage::isQuarterFrame() const noexcept { return *getRawData() == 0xf1; } int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; } int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) getRawData()[1]) & 0x0f; } MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept { return MidiMessage (0xf1, (sequenceNumber << 4) | value); } bool MidiMessage::isFullFrame() const noexcept { const uint8* const data = getRawData(); return data[0] == 0xf0 && data[1] == 0x7f && size >= 10 && data[3] == 0x01 && data[4] == 0x01; } void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds, int& frames, MidiMessage::SmpteTimecodeType& timecodeType) const noexcept { jassert (isFullFrame()); const uint8* const data = getRawData(); timecodeType = (SmpteTimecodeType) (data[5] >> 5); hours = data[5] & 0x1f; minutes = data[6]; seconds = data[7]; frames = data[8]; } MidiMessage MidiMessage::fullFrame (const int hours, const int minutes, const int seconds, const int frames, MidiMessage::SmpteTimecodeType timecodeType) { const uint8 d[] = { 0xf0, 0x7f, 0x7f, 0x01, 0x01, (uint8) ((hours & 0x01f) | (timecodeType << 5)), (uint8) minutes, (uint8) seconds, (uint8) frames, 0xf7 }; return MidiMessage (d, 10, 0.0); } bool MidiMessage::isMidiMachineControlMessage() const noexcept { const uint8* const data = getRawData(); return data[0] == 0xf0 && data[1] == 0x7f && data[3] == 0x06 && size > 5; } MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const noexcept { jassert (isMidiMachineControlMessage()); return (MidiMachineControlCommand) getRawData()[4]; } MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command) { const uint8 d[] = { 0xf0, 0x7f, 0, 6, (uint8) command, 0xf7 }; return MidiMessage (d, 6, 0.0); } //============================================================================== bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept { const uint8* const data = getRawData(); if (size >= 12 && data[0] == 0xf0 && data[1] == 0x7f && data[3] == 0x06 && data[4] == 0x44 && data[5] == 0x06 && data[6] == 0x01) { hours = data[7] % 24; // (that some machines send out hours > 24) minutes = data[8]; seconds = data[9]; frames = data[10]; return true; } return false; } MidiMessage MidiMessage::midiMachineControlGoto (int hours, int minutes, int seconds, int frames) { const uint8 d[] = { 0xf0, 0x7f, 0, 6, 0x44, 6, 1, (uint8) hours, (uint8) minutes, (uint8) seconds, (uint8) frames, 0xf7 }; return MidiMessage (d, 12, 0.0); } //============================================================================== String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC) { static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" }; if (isPositiveAndBelow (note, (int) 128)) { String s (useSharps ? sharpNoteNames [note % 12] : flatNoteNames [note % 12]); if (includeOctaveNumber) s << (note / 12 + (octaveNumForMiddleC - 5)); return s; } return String::empty; } double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept { return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0); } bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept { return ((1 << (noteNumber % 12)) & 0x054a) != 0; } const char* MidiMessage::getGMInstrumentName (const int n) { static const char* names[] = { NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"), NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"), NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"), NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"), NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"), NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"), NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"), NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"), NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"), NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"), NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"), NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"), NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"), NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"), NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"), NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"), NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"), NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"), NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"), NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"), NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"), NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"), NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"), NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"), NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"), NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"), NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"), NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"), NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"), NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"), NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"), NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; } const char* MidiMessage::getGMInstrumentBankName (const int n) { static const char* names[] = { NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"), NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"), NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"), NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; } const char* MidiMessage::getRhythmInstrumentName (const int n) { static const char* names[] = { NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"), NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"), NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"), NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"), NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"), NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"), NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"), NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"), NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"), NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"), NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"), NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle") }; return (n >= 35 && n <= 81) ? names [n - 35] : nullptr; } const char* MidiMessage::getControllerName (const int n) { static const char* names[] = { NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"), nullptr, NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"), NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"), nullptr, NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"), NEEDS_TRANS("Effect Control 2 (coarse)"), nullptr, nullptr, NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"), NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"), nullptr, NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"), NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"), NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"), NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"), NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"), NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"), NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"), NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"), NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"), NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"), NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h000066400000000000000000001067441320201440200315430ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIMESSAGE_H_INCLUDED #define JUCE_MIDIMESSAGE_H_INCLUDED //============================================================================== /** Encapsulates a MIDI message. @see MidiMessageSequence, MidiOutput, MidiInput */ class JUCE_API MidiMessage { public: //============================================================================== /** Creates a 3-byte short midi message. @param byte1 message byte 1 @param byte2 message byte 2 @param byte3 message byte 3 @param timeStamp the time to give the midi message - this value doesn't use any particular units, so will be application-specific */ MidiMessage (int byte1, int byte2, int byte3, double timeStamp = 0) noexcept; /** Creates a 2-byte short midi message. @param byte1 message byte 1 @param byte2 message byte 2 @param timeStamp the time to give the midi message - this value doesn't use any particular units, so will be application-specific */ MidiMessage (int byte1, int byte2, double timeStamp = 0) noexcept; /** Creates a 1-byte short midi message. @param byte1 message byte 1 @param timeStamp the time to give the midi message - this value doesn't use any particular units, so will be application-specific */ MidiMessage (int byte1, double timeStamp = 0) noexcept; /** Creates a midi message from a block of data. */ MidiMessage (const void* data, int numBytes, double timeStamp = 0); /** Reads the next midi message from some data. This will read as many bytes from a data stream as it needs to make a complete message, and will return the number of bytes it used. This lets you read a sequence of midi messages from a file or stream. @param data the data to read from @param maxBytesToUse the maximum number of bytes it's allowed to read @param numBytesUsed returns the number of bytes that were actually needed @param lastStatusByte in a sequence of midi messages, the initial byte can be dropped from a message if it's the same as the first byte of the previous message, so this lets you supply the byte to use if the first byte of the message has in fact been dropped. @param timeStamp the time to give the midi message - this value doesn't use any particular units, so will be application-specific @param sysexHasEmbeddedLength when reading sysexes, this flag indicates whether to expect the data to begin with a variable-length field indicating its size */ MidiMessage (const void* data, int maxBytesToUse, int& numBytesUsed, uint8 lastStatusByte, double timeStamp = 0, bool sysexHasEmbeddedLength = true); /** Creates an active-sense message. Since the MidiMessage has to contain a valid message, this default constructor just initialises it with an empty sysex message. */ MidiMessage() noexcept; /** Creates a copy of another midi message. */ MidiMessage (const MidiMessage&); /** Creates a copy of another midi message, with a different timestamp. */ MidiMessage (const MidiMessage&, double newTimeStamp); /** Destructor. */ ~MidiMessage(); /** Copies this message from another one. */ MidiMessage& operator= (const MidiMessage& other); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MidiMessage (MidiMessage&&) noexcept; MidiMessage& operator= (MidiMessage&&) noexcept; #endif //============================================================================== /** Returns a pointer to the raw midi data. @see getRawDataSize */ const uint8* getRawData() const noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } /** Returns the number of bytes of data in the message. @see getRawData */ int getRawDataSize() const noexcept { return size; } //============================================================================== /** Returns the timestamp associated with this message. The exact meaning of this time and its units will vary, as messages are used in a variety of different contexts. If you're getting the message from a midi file, this could be a time in seconds, or a number of ticks - see MidiFile::convertTimestampTicksToSeconds(). If the message is being used in a MidiBuffer, it might indicate the number of audio samples from the start of the buffer. If the message was created by a MidiInput, see MidiInputCallback::handleIncomingMidiMessage() for details of the way that it initialises this value. @see setTimeStamp, addToTimeStamp */ double getTimeStamp() const noexcept { return timeStamp; } /** Changes the message's associated timestamp. The units for the timestamp will be application-specific - see the notes for getTimeStamp(). @see addToTimeStamp, getTimeStamp */ void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; } /** Adds a value to the message's timestamp. The units for the timestamp will be application-specific. */ void addToTimeStamp (double delta) noexcept { timeStamp += delta; } //============================================================================== /** Returns the midi channel associated with the message. @returns a value 1 to 16 if the message has a channel, or 0 if it hasn't (e.g. if it's a sysex) @see isForChannel, setChannel */ int getChannel() const noexcept; /** Returns true if the message applies to the given midi channel. @param channelNumber the channel number to look for, in the range 1 to 16 @see getChannel, setChannel */ bool isForChannel (int channelNumber) const noexcept; /** Changes the message's midi channel. This won't do anything for non-channel messages like sysexes. @param newChannelNumber the channel number to change it to, in the range 1 to 16 */ void setChannel (int newChannelNumber) noexcept; //============================================================================== /** Returns true if this is a system-exclusive message. */ bool isSysEx() const noexcept; /** Returns a pointer to the sysex data inside the message. If this event isn't a sysex event, it'll return 0. @see getSysExDataSize */ const uint8* getSysExData() const noexcept; /** Returns the size of the sysex data. This value excludes the 0xf0 header byte and the 0xf7 at the end. @see getSysExData */ int getSysExDataSize() const noexcept; //============================================================================== /** Returns true if this message is a 'key-down' event. @param returnTrueForVelocity0 if true, then if this event is a note-on with velocity 0, it will still be considered to be a note-on and the method will return true. If returnTrueForVelocity0 is false, then if this is a note-on event with velocity 0, it'll be regarded as a note-off, and the method will return false @see isNoteOff, getNoteNumber, getVelocity, noteOn */ bool isNoteOn (bool returnTrueForVelocity0 = false) const noexcept; /** Creates a key-down message (using a floating-point velocity). @param channel the midi channel, in the range 1 to 16 @param noteNumber the key number, 0 to 127 @param velocity in the range 0 to 1.0 @see isNoteOn */ static MidiMessage noteOn (int channel, int noteNumber, float velocity) noexcept; /** Creates a key-down message (using an integer velocity). @param channel the midi channel, in the range 1 to 16 @param noteNumber the key number, 0 to 127 @param velocity in the range 0 to 127 @see isNoteOn */ static MidiMessage noteOn (int channel, int noteNumber, uint8 velocity) noexcept; /** Returns true if this message is a 'key-up' event. If returnTrueForNoteOnVelocity0 is true, then his will also return true for a note-on event with a velocity of 0. @see isNoteOn, getNoteNumber, getVelocity, noteOff */ bool isNoteOff (bool returnTrueForNoteOnVelocity0 = true) const noexcept; /** Creates a key-up message. @param channel the midi channel, in the range 1 to 16 @param noteNumber the key number, 0 to 127 @param velocity in the range 0 to 127 @see isNoteOff */ static MidiMessage noteOff (int channel, int noteNumber, uint8 velocity = 0) noexcept; /** Returns true if this message is a 'key-down' or 'key-up' event. @see isNoteOn, isNoteOff */ bool isNoteOnOrOff() const noexcept; /** Returns the midi note number for note-on and note-off messages. If the message isn't a note-on or off, the value returned is undefined. @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber */ int getNoteNumber() const noexcept; /** Changes the midi note number of a note-on or note-off message. If the message isn't a note on or off, this will do nothing. */ void setNoteNumber (int newNoteNumber) noexcept; //============================================================================== /** Returns the velocity of a note-on or note-off message. The value returned will be in the range 0 to 127. If the message isn't a note-on or off event, it will return 0. @see getFloatVelocity */ uint8 getVelocity() const noexcept; /** Returns the velocity of a note-on or note-off message. The value returned will be in the range 0 to 1.0 If the message isn't a note-on or off event, it will return 0. @see getVelocity, setVelocity */ float getFloatVelocity() const noexcept; /** Changes the velocity of a note-on or note-off message. If the message isn't a note on or off, this will do nothing. @param newVelocity the new velocity, in the range 0 to 1.0 @see getFloatVelocity, multiplyVelocity */ void setVelocity (float newVelocity) noexcept; /** Multiplies the velocity of a note-on or note-off message by a given amount. If the message isn't a note on or off, this will do nothing. @param scaleFactor the value by which to multiply the velocity @see setVelocity */ void multiplyVelocity (float scaleFactor) noexcept; //============================================================================== /** Returns true if this message is a 'sustain pedal down' controller message. */ bool isSustainPedalOn() const noexcept; /** Returns true if this message is a 'sustain pedal up' controller message. */ bool isSustainPedalOff() const noexcept; /** Returns true if this message is a 'sostenuto pedal down' controller message. */ bool isSostenutoPedalOn() const noexcept; /** Returns true if this message is a 'sostenuto pedal up' controller message. */ bool isSostenutoPedalOff() const noexcept; /** Returns true if this message is a 'soft pedal down' controller message. */ bool isSoftPedalOn() const noexcept; /** Returns true if this message is a 'soft pedal up' controller message. */ bool isSoftPedalOff() const noexcept; //============================================================================== /** Returns true if the message is a program (patch) change message. @see getProgramChangeNumber, getGMInstrumentName */ bool isProgramChange() const noexcept; /** Returns the new program number of a program change message. If the message isn't a program change, the value returned is undefined. @see isProgramChange, getGMInstrumentName */ int getProgramChangeNumber() const noexcept; /** Creates a program-change message. @param channel the midi channel, in the range 1 to 16 @param programNumber the midi program number, 0 to 127 @see isProgramChange, getGMInstrumentName */ static MidiMessage programChange (int channel, int programNumber) noexcept; //============================================================================== /** Returns true if the message is a pitch-wheel move. @see getPitchWheelValue, pitchWheel */ bool isPitchWheel() const noexcept; /** Returns the pitch wheel position from a pitch-wheel move message. The value returned is a 14-bit number from 0 to 0x3fff, indicating the wheel position. If called for messages which aren't pitch wheel events, the number returned will be nonsense. @see isPitchWheel */ int getPitchWheelValue() const noexcept; /** Creates a pitch-wheel move message. @param channel the midi channel, in the range 1 to 16 @param position the wheel position, in the range 0 to 16383 @see isPitchWheel */ static MidiMessage pitchWheel (int channel, int position) noexcept; //============================================================================== /** Returns true if the message is an aftertouch event. For aftertouch events, use the getNoteNumber() method to find out the key that it applies to, and getAftertouchValue() to find out the amount. Use getChannel() to find out the channel. @see getAftertouchValue, getNoteNumber */ bool isAftertouch() const noexcept; /** Returns the amount of aftertouch from an aftertouch messages. The value returned is in the range 0 to 127, and will be nonsense for messages other than aftertouch messages. @see isAftertouch */ int getAfterTouchValue() const noexcept; /** Creates an aftertouch message. @param channel the midi channel, in the range 1 to 16 @param noteNumber the key number, 0 to 127 @param aftertouchAmount the amount of aftertouch, 0 to 127 @see isAftertouch */ static MidiMessage aftertouchChange (int channel, int noteNumber, int aftertouchAmount) noexcept; /** Returns true if the message is a channel-pressure change event. This is like aftertouch, but common to the whole channel rather than a specific note. Use getChannelPressureValue() to find out the pressure, and getChannel() to find out the channel. @see channelPressureChange */ bool isChannelPressure() const noexcept; /** Returns the pressure from a channel pressure change message. @returns the pressure, in the range 0 to 127 @see isChannelPressure, channelPressureChange */ int getChannelPressureValue() const noexcept; /** Creates a channel-pressure change event. @param channel the midi channel: 1 to 16 @param pressure the pressure, 0 to 127 @see isChannelPressure */ static MidiMessage channelPressureChange (int channel, int pressure) noexcept; //============================================================================== /** Returns true if this is a midi controller message. @see getControllerNumber, getControllerValue, controllerEvent */ bool isController() const noexcept; /** Returns the controller number of a controller message. The name of the controller can be looked up using the getControllerName() method. Note that the value returned is invalid for messages that aren't controller changes. @see isController, getControllerName, getControllerValue */ int getControllerNumber() const noexcept; /** Returns the controller value from a controller message. A value 0 to 127 is returned to indicate the new controller position. Note that the value returned is invalid for messages that aren't controller changes. @see isController, getControllerNumber */ int getControllerValue() const noexcept; /** Returns true if this message is a controller message and if it has the specified controller type. */ bool isControllerOfType (int controllerType) const noexcept; /** Creates a controller message. @param channel the midi channel, in the range 1 to 16 @param controllerType the type of controller @param value the controller value @see isController */ static MidiMessage controllerEvent (int channel, int controllerType, int value) noexcept; /** Checks whether this message is an all-notes-off message. @see allNotesOff */ bool isAllNotesOff() const noexcept; /** Checks whether this message is an all-sound-off message. @see allSoundOff */ bool isAllSoundOff() const noexcept; /** Creates an all-notes-off message. @param channel the midi channel, in the range 1 to 16 @see isAllNotesOff */ static MidiMessage allNotesOff (int channel) noexcept; /** Creates an all-sound-off message. @param channel the midi channel, in the range 1 to 16 @see isAllSoundOff */ static MidiMessage allSoundOff (int channel) noexcept; /** Creates an all-controllers-off message. @param channel the midi channel, in the range 1 to 16 */ static MidiMessage allControllersOff (int channel) noexcept; //============================================================================== /** Returns true if this event is a meta-event. Meta-events are things like tempo changes, track names, etc. @see getMetaEventType, isTrackMetaEvent, isEndOfTrackMetaEvent, isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, isKeySignatureMetaEvent, isMidiChannelMetaEvent */ bool isMetaEvent() const noexcept; /** Returns a meta-event's type number. If the message isn't a meta-event, this will return -1. @see isMetaEvent, isTrackMetaEvent, isEndOfTrackMetaEvent, isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, isKeySignatureMetaEvent, isMidiChannelMetaEvent */ int getMetaEventType() const noexcept; /** Returns a pointer to the data in a meta-event. @see isMetaEvent, getMetaEventLength */ const uint8* getMetaEventData() const noexcept; /** Returns the length of the data for a meta-event. @see isMetaEvent, getMetaEventData */ int getMetaEventLength() const noexcept; //============================================================================== /** Returns true if this is a 'track' meta-event. */ bool isTrackMetaEvent() const noexcept; /** Returns true if this is an 'end-of-track' meta-event. */ bool isEndOfTrackMetaEvent() const noexcept; /** Creates an end-of-track meta-event. @see isEndOfTrackMetaEvent */ static MidiMessage endOfTrack() noexcept; /** Returns true if this is an 'track name' meta-event. You can use the getTextFromTextMetaEvent() method to get the track's name. */ bool isTrackNameEvent() const noexcept; /** Returns true if this is a 'text' meta-event. @see getTextFromTextMetaEvent */ bool isTextMetaEvent() const noexcept; /** Returns the text from a text meta-event. @see isTextMetaEvent */ String getTextFromTextMetaEvent() const; /** Creates a text meta-event. */ static MidiMessage textMetaEvent (int type, StringRef text); //============================================================================== /** Returns true if this is a 'tempo' meta-event. @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote */ bool isTempoMetaEvent() const noexcept; /** Returns the tick length from a tempo meta-event. @param timeFormat the 16-bit time format value from the midi file's header. @returns the tick length (in seconds). @see isTempoMetaEvent */ double getTempoMetaEventTickLength (short timeFormat) const noexcept; /** Calculates the seconds-per-quarter-note from a tempo meta-event. @see isTempoMetaEvent, getTempoMetaEventTickLength */ double getTempoSecondsPerQuarterNote() const noexcept; /** Creates a tempo meta-event. @see isTempoMetaEvent */ static MidiMessage tempoMetaEvent (int microsecondsPerQuarterNote) noexcept; //============================================================================== /** Returns true if this is a 'time-signature' meta-event. @see getTimeSignatureInfo */ bool isTimeSignatureMetaEvent() const noexcept; /** Returns the time-signature values from a time-signature meta-event. @see isTimeSignatureMetaEvent */ void getTimeSignatureInfo (int& numerator, int& denominator) const noexcept; /** Creates a time-signature meta-event. @see isTimeSignatureMetaEvent */ static MidiMessage timeSignatureMetaEvent (int numerator, int denominator); //============================================================================== /** Returns true if this is a 'key-signature' meta-event. @see getKeySignatureNumberOfSharpsOrFlats, isKeySignatureMajorKey */ bool isKeySignatureMetaEvent() const noexcept; /** Returns the key from a key-signature meta-event. This method must only be called if isKeySignatureMetaEvent() is true. A positive number here indicates the number of sharps in the key signature, and a negative number indicates a number of flats. So e.g. 3 = F# + C# + G#, -2 = Bb + Eb @see isKeySignatureMetaEvent, isKeySignatureMajorKey */ int getKeySignatureNumberOfSharpsOrFlats() const noexcept; /** Returns true if this key-signature event is major, or false if it's minor. This method must only be called if isKeySignatureMetaEvent() is true. */ bool isKeySignatureMajorKey() const noexcept; /** Creates a key-signature meta-event. @param numberOfSharpsOrFlats if positive, this indicates the number of sharps in the key; if negative, the number of flats @param isMinorKey if true, the key is minor; if false, it is major @see isKeySignatureMetaEvent */ static MidiMessage keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey); //============================================================================== /** Returns true if this is a 'channel' meta-event. A channel meta-event specifies the midi channel that should be used for subsequent meta-events. @see getMidiChannelMetaEventChannel */ bool isMidiChannelMetaEvent() const noexcept; /** Returns the channel number from a channel meta-event. @returns the channel, in the range 1 to 16. @see isMidiChannelMetaEvent */ int getMidiChannelMetaEventChannel() const noexcept; /** Creates a midi channel meta-event. @param channel the midi channel, in the range 1 to 16 @see isMidiChannelMetaEvent */ static MidiMessage midiChannelMetaEvent (int channel) noexcept; //============================================================================== /** Returns true if this is an active-sense message. */ bool isActiveSense() const noexcept; //============================================================================== /** Returns true if this is a midi start event. @see midiStart */ bool isMidiStart() const noexcept; /** Creates a midi start event. */ static MidiMessage midiStart() noexcept; /** Returns true if this is a midi continue event. @see midiContinue */ bool isMidiContinue() const noexcept; /** Creates a midi continue event. */ static MidiMessage midiContinue() noexcept; /** Returns true if this is a midi stop event. @see midiStop */ bool isMidiStop() const noexcept; /** Creates a midi stop event. */ static MidiMessage midiStop() noexcept; /** Returns true if this is a midi clock event. @see midiClock, songPositionPointer */ bool isMidiClock() const noexcept; /** Creates a midi clock event. */ static MidiMessage midiClock() noexcept; /** Returns true if this is a song-position-pointer message. @see getSongPositionPointerMidiBeat, songPositionPointer */ bool isSongPositionPointer() const noexcept; /** Returns the midi beat-number of a song-position-pointer message. @see isSongPositionPointer, songPositionPointer */ int getSongPositionPointerMidiBeat() const noexcept; /** Creates a song-position-pointer message. The position is a number of midi beats from the start of the song, where 1 midi beat is 6 midi clocks, and there are 24 midi clocks in a quarter-note. So there are 4 midi beats in a quarter-note. @see isSongPositionPointer, getSongPositionPointerMidiBeat */ static MidiMessage songPositionPointer (int positionInMidiBeats) noexcept; //============================================================================== /** Returns true if this is a quarter-frame midi timecode message. @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue */ bool isQuarterFrame() const noexcept; /** Returns the sequence number of a quarter-frame midi timecode message. This will be a value between 0 and 7. @see isQuarterFrame, getQuarterFrameValue, quarterFrame */ int getQuarterFrameSequenceNumber() const noexcept; /** Returns the value from a quarter-frame message. This will be the lower nybble of the message's data-byte, a value between 0 and 15 */ int getQuarterFrameValue() const noexcept; /** Creates a quarter-frame MTC message. @param sequenceNumber a value 0 to 7 for the upper nybble of the message's data byte @param value a value 0 to 15 for the lower nybble of the message's data byte */ static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept; /** SMPTE timecode types. Used by the getFullFrameParameters() and fullFrame() methods. */ enum SmpteTimecodeType { fps24 = 0, fps25 = 1, fps30drop = 2, fps30 = 3 }; /** Returns true if this is a full-frame midi timecode message. */ bool isFullFrame() const noexcept; /** Extracts the timecode information from a full-frame midi timecode message. You should only call this on messages where you've used isFullFrame() to check that they're the right kind. */ void getFullFrameParameters (int& hours, int& minutes, int& seconds, int& frames, SmpteTimecodeType& timecodeType) const noexcept; /** Creates a full-frame MTC message. */ static MidiMessage fullFrame (int hours, int minutes, int seconds, int frames, SmpteTimecodeType timecodeType); //============================================================================== /** Types of MMC command. @see isMidiMachineControlMessage, getMidiMachineControlCommand, midiMachineControlCommand */ enum MidiMachineControlCommand { mmc_stop = 1, mmc_play = 2, mmc_deferredplay = 3, mmc_fastforward = 4, mmc_rewind = 5, mmc_recordStart = 6, mmc_recordStop = 7, mmc_pause = 9 }; /** Checks whether this is an MMC message. If it is, you can use the getMidiMachineControlCommand() to find out its type. */ bool isMidiMachineControlMessage() const noexcept; /** For an MMC message, this returns its type. Make sure it's actually an MMC message with isMidiMachineControlMessage() before calling this method. */ MidiMachineControlCommand getMidiMachineControlCommand() const noexcept; /** Creates an MMC message. */ static MidiMessage midiMachineControlCommand (MidiMachineControlCommand command); /** Checks whether this is an MMC "goto" message. If it is, the parameters passed-in are set to the time that the message contains. @see midiMachineControlGoto */ bool isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept; /** Creates an MMC "goto" message. This messages tells the device to go to a specific frame. @see isMidiMachineControlGoto */ static MidiMessage midiMachineControlGoto (int hours, int minutes, int seconds, int frames); //============================================================================== /** Creates a master-volume change message. @param volume the volume, 0 to 1.0 */ static MidiMessage masterVolume (float volume); //============================================================================== /** Creates a system-exclusive message. The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7. */ static MidiMessage createSysExMessage (const void* sysexData, int dataSize); //============================================================================== /** Reads a midi variable-length integer. @param data the data to read the number from @param numBytesUsed on return, this will be set to the number of bytes that were read */ static int readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept; /** Based on the first byte of a short midi message, this uses a lookup table to return the message length (either 1, 2, or 3 bytes). The value passed in must be 0x80 or higher. */ static int getMessageLengthFromFirstByte (const uint8 firstByte) noexcept; //============================================================================== /** Returns the name of a midi note number. E.g "C", "D#", etc. @param noteNumber the midi note number, 0 to 127 @param useSharps if true, sharpened notes are used, e.g. "C#", otherwise they'll be flattened, e.g. "Db" @param includeOctaveNumber if true, the octave number will be appended to the string, e.g. "C#4" @param octaveNumForMiddleC if an octave number is being appended, this indicates the number that will be used for middle C's octave @see getMidiNoteInHertz */ static String getMidiNoteName (int noteNumber, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC); /** Returns the frequency of a midi note number. The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch. @see getMidiNoteName */ static double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept; /** Returns true if the given midi note number is a black key. */ static bool isMidiNoteBlack (int noteNumber) noexcept; /** Returns the standard name of a GM instrument, or nullptr if unknown for this index. @param midiInstrumentNumber the program number 0 to 127 @see getProgramChangeNumber */ static const char* getGMInstrumentName (int midiInstrumentNumber); /** Returns the name of a bank of GM instruments, or nullptr if unknown for this bank number. @param midiBankNumber the bank, 0 to 15 */ static const char* getGMInstrumentBankName (int midiBankNumber); /** Returns the standard name of a channel 10 percussion sound, or nullptr if unknown for this note number. @param midiNoteNumber the key number, 35 to 81 */ static const char* getRhythmInstrumentName (int midiNoteNumber); /** Returns the name of a controller type number, or nullptr if unknown for this controller number. @see getControllerNumber */ static const char* getControllerName (int controllerNumber); private: //============================================================================== double timeStamp; HeapBlock allocatedData; int size; #ifndef DOXYGEN union { uint8 asBytes[4]; uint32 asInt32; } preallocatedData; #endif inline uint8* getData() noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } uint8* allocateSpace (int); }; #endif // JUCE_MIDIMESSAGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp000066400000000000000000000236111320201440200335560ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ MidiMessageSequence::MidiMessageSequence() { } MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) { list.addCopiesOf (other.list); updateMatchedPairs(); } MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other) { MidiMessageSequence otherCopy (other); swapWith (otherCopy); return *this; } void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept { list.swapWith (other.list); } MidiMessageSequence::~MidiMessageSequence() { } void MidiMessageSequence::clear() { list.clear(); } int MidiMessageSequence::getNumEvents() const noexcept { return list.size(); } MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const noexcept { return list [index]; } double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const noexcept { if (const MidiEventHolder* const meh = list [index]) if (meh->noteOffObject != nullptr) return meh->noteOffObject->message.getTimeStamp(); return 0.0; } int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcept { if (const MidiEventHolder* const meh = list [index]) return list.indexOf (meh->noteOffObject); return -1; } int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const noexcept { return list.indexOf (event); } int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const noexcept { const int numEvents = list.size(); int i; for (i = 0; i < numEvents; ++i) if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp) break; return i; } //============================================================================== double MidiMessageSequence::getStartTime() const noexcept { return getEventTime (0); } double MidiMessageSequence::getEndTime() const noexcept { return getEventTime (list.size() - 1); } double MidiMessageSequence::getEventTime (const int index) const noexcept { if (const MidiEventHolder* const meh = list [index]) return meh->message.getTimeStamp(); return 0.0; } //============================================================================== MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage, double timeAdjustment) { MidiEventHolder* const newOne = new MidiEventHolder (newMessage); timeAdjustment += newMessage.getTimeStamp(); newOne->message.setTimeStamp (timeAdjustment); int i; for (i = list.size(); --i >= 0;) if (list.getUnchecked(i)->message.getTimeStamp() <= timeAdjustment) break; list.insert (i + 1, newOne); return newOne; } void MidiMessageSequence::deleteEvent (const int index, const bool deleteMatchingNoteUp) { if (isPositiveAndBelow (index, list.size())) { if (deleteMatchingNoteUp) deleteEvent (getIndexOfMatchingKeyUp (index), false); list.remove (index); } } struct MidiMessageSequenceSorter { static int compareElements (const MidiMessageSequence::MidiEventHolder* const first, const MidiMessageSequence::MidiEventHolder* const second) noexcept { const double diff = first->message.getTimeStamp() - second->message.getTimeStamp(); return (diff > 0) - (diff < 0); } }; void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double timeAdjustment, double firstAllowableTime, double endOfAllowableDestTimes) { firstAllowableTime -= timeAdjustment; endOfAllowableDestTimes -= timeAdjustment; for (int i = 0; i < other.list.size(); ++i) { const MidiMessage& m = other.list.getUnchecked(i)->message; const double t = m.getTimeStamp(); if (t >= firstAllowableTime && t < endOfAllowableDestTimes) { MidiEventHolder* const newOne = new MidiEventHolder (m); newOne->message.setTimeStamp (timeAdjustment + t); list.add (newOne); } } sort(); } //============================================================================== void MidiMessageSequence::sort() noexcept { MidiMessageSequenceSorter sorter; list.sort (sorter, true); } void MidiMessageSequence::updateMatchedPairs() noexcept { for (int i = 0; i < list.size(); ++i) { MidiEventHolder* const meh = list.getUnchecked(i); const MidiMessage& m1 = meh->message; if (m1.isNoteOn()) { meh->noteOffObject = nullptr; const int note = m1.getNoteNumber(); const int chan = m1.getChannel(); const int len = list.size(); for (int j = i + 1; j < len; ++j) { const MidiMessage& m = list.getUnchecked(j)->message; if (m.getNoteNumber() == note && m.getChannel() == chan) { if (m.isNoteOff()) { meh->noteOffObject = list[j]; break; } else if (m.isNoteOn()) { MidiEventHolder* const newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note)); list.insert (j, newEvent); newEvent->message.setTimeStamp (m.getTimeStamp()); meh->noteOffObject = newEvent; break; } } } } } } void MidiMessageSequence::addTimeToMessages (const double delta) noexcept { for (int i = list.size(); --i >= 0;) { MidiMessage& mm = list.getUnchecked(i)->message; mm.setTimeStamp (mm.getTimeStamp() + delta); } } //============================================================================== void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract, MidiMessageSequence& destSequence, const bool alsoIncludeMetaEvents) const { for (int i = 0; i < list.size(); ++i) { const MidiMessage& mm = list.getUnchecked(i)->message; if (mm.isForChannel (channelNumberToExtract) || (alsoIncludeMetaEvents && mm.isMetaEvent())) destSequence.addEvent (mm); } } void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const { for (int i = 0; i < list.size(); ++i) { const MidiMessage& mm = list.getUnchecked(i)->message; if (mm.isSysEx()) destSequence.addEvent (mm); } } void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove) { for (int i = list.size(); --i >= 0;) if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove)) list.remove(i); } void MidiMessageSequence::deleteSysExMessages() { for (int i = list.size(); --i >= 0;) if (list.getUnchecked(i)->message.isSysEx()) list.remove(i); } //============================================================================== void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, const double time, Array& dest) { bool doneProg = false; bool donePitchWheel = false; bool doneControllers[128] = { 0 }; for (int i = list.size(); --i >= 0;) { const MidiMessage& mm = list.getUnchecked(i)->message; if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) { if (mm.isProgramChange() && ! doneProg) { doneProg = true; dest.add (MidiMessage (mm, 0.0)); } else if (mm.isPitchWheel() && ! donePitchWheel) { donePitchWheel = true; dest.add (MidiMessage (mm, 0.0)); } else if (mm.isController()) { const int controllerNumber = mm.getControllerNumber(); jassert (isPositiveAndBelow (controllerNumber, 128)); if (! doneControllers[controllerNumber]) { doneControllers[controllerNumber] = true; dest.add (MidiMessage (mm, 0.0)); } } } } } //============================================================================== MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm), noteOffObject (nullptr) { } MidiMessageSequence::MidiEventHolder::~MidiEventHolder() { } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h000066400000000000000000000263501320201440200332260ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED #define JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED //============================================================================== /** A sequence of timestamped midi messages. This allows the sequence to be manipulated, and also to be read from and written to a standard midi file. @see MidiMessage, MidiFile */ class JUCE_API MidiMessageSequence { public: //============================================================================== /** Creates an empty midi sequence object. */ MidiMessageSequence(); /** Creates a copy of another sequence. */ MidiMessageSequence (const MidiMessageSequence&); /** Replaces this sequence with another one. */ MidiMessageSequence& operator= (const MidiMessageSequence&); /** Destructor. */ ~MidiMessageSequence(); //============================================================================== /** Structure used to hold midi events in the sequence. These structures act as 'handles' on the events as they are moved about in the list, and make it quick to find the matching note-offs for note-on events. @see MidiMessageSequence::getEventPointer */ class MidiEventHolder { public: //============================================================================== /** Destructor. */ ~MidiEventHolder(); /** The message itself, whose timestamp is used to specify the event's time. */ MidiMessage message; /** The matching note-off event (if this is a note-on event). If this isn't a note-on, this pointer will be nullptr. Use the MidiMessageSequence::updateMatchedPairs() method to keep these note-offs up-to-date after events have been moved around in the sequence or deleted. */ MidiEventHolder* noteOffObject; private: //============================================================================== friend class MidiMessageSequence; MidiEventHolder (const MidiMessage&); JUCE_LEAK_DETECTOR (MidiEventHolder) }; //============================================================================== /** Clears the sequence. */ void clear(); /** Returns the number of events in the sequence. */ int getNumEvents() const noexcept; /** Returns a pointer to one of the events. */ MidiEventHolder* getEventPointer (int index) const noexcept; /** Returns the time of the note-up that matches the note-on at this index. If the event at this index isn't a note-on, it'll just return 0. @see MidiMessageSequence::MidiEventHolder::noteOffObject */ double getTimeOfMatchingKeyUp (int index) const noexcept; /** Returns the index of the note-up that matches the note-on at this index. If the event at this index isn't a note-on, it'll just return -1. @see MidiMessageSequence::MidiEventHolder::noteOffObject */ int getIndexOfMatchingKeyUp (int index) const noexcept; /** Returns the index of an event. */ int getIndexOf (MidiEventHolder* event) const noexcept; /** Returns the index of the first event on or after the given timestamp. If the time is beyond the end of the sequence, this will return the number of events. */ int getNextIndexAtTime (double timeStamp) const noexcept; //============================================================================== /** Returns the timestamp of the first event in the sequence. @see getEndTime */ double getStartTime() const noexcept; /** Returns the timestamp of the last event in the sequence. @see getStartTime */ double getEndTime() const noexcept; /** Returns the timestamp of the event at a given index. If the index is out-of-range, this will return 0.0 */ double getEventTime (int index) const noexcept; //============================================================================== /** Inserts a midi message into the sequence. The index at which the new message gets inserted will depend on its timestamp, because the sequence is kept sorted. Remember to call updateMatchedPairs() after adding note-on events. @param newMessage the new message to add (an internal copy will be made) @param timeAdjustment an optional value to add to the timestamp of the message that will be inserted @see updateMatchedPairs */ MidiEventHolder* addEvent (const MidiMessage& newMessage, double timeAdjustment = 0); /** Deletes one of the events in the sequence. Remember to call updateMatchedPairs() after removing events. @param index the index of the event to delete @param deleteMatchingNoteUp whether to also remove the matching note-off if the event you're removing is a note-on */ void deleteEvent (int index, bool deleteMatchingNoteUp); /** Merges another sequence into this one. Remember to call updateMatchedPairs() after using this method. @param other the sequence to add from @param timeAdjustmentDelta an amount to add to the timestamps of the midi events as they are read from the other sequence @param firstAllowableDestTime events will not be added if their time is earlier than this time. (This is after their time has been adjusted by the timeAdjustmentDelta) @param endOfAllowableDestTimes events will not be added if their time is equal to or greater than this time. (This is after their time has been adjusted by the timeAdjustmentDelta) */ void addSequence (const MidiMessageSequence& other, double timeAdjustmentDelta, double firstAllowableDestTime, double endOfAllowableDestTimes); //============================================================================== /** Makes sure all the note-on and note-off pairs are up-to-date. Call this after re-ordering messages or deleting/adding messages, and it will scan the list and make sure all the note-offs in the MidiEventHolder structures are pointing at the correct ones. */ void updateMatchedPairs() noexcept; /** Forces a sort of the sequence. You may need to call this if you've manually modified the timestamps of some events such that the overall order now needs updating. */ void sort() noexcept; //============================================================================== /** Copies all the messages for a particular midi channel to another sequence. @param channelNumberToExtract the midi channel to look for, in the range 1 to 16 @param destSequence the sequence that the chosen events should be copied to @param alsoIncludeMetaEvents if true, any meta-events (which don't apply to a specific channel) will also be copied across. @see extractSysExMessages */ void extractMidiChannelMessages (int channelNumberToExtract, MidiMessageSequence& destSequence, bool alsoIncludeMetaEvents) const; /** Copies all midi sys-ex messages to another sequence. @param destSequence this is the sequence to which any sys-exes in this sequence will be added @see extractMidiChannelMessages */ void extractSysExMessages (MidiMessageSequence& destSequence) const; /** Removes any messages in this sequence that have a specific midi channel. @param channelNumberToRemove the midi channel to look for, in the range 1 to 16 */ void deleteMidiChannelMessages (int channelNumberToRemove); /** Removes any sys-ex messages from this sequence. */ void deleteSysExMessages(); /** Adds an offset to the timestamps of all events in the sequence. @param deltaTime the amount to add to each timestamp. */ void addTimeToMessages (double deltaTime) noexcept; //============================================================================== /** Scans through the sequence to determine the state of any midi controllers at a given time. This will create a sequence of midi controller changes that can be used to set all midi controllers to the state they would be in at the specified time within this sequence. As well as controllers, it will also recreate the midi program number and pitch bend position. @param channelNumber the midi channel to look for, in the range 1 to 16. Controllers for other channels will be ignored. @param time the time at which you want to find out the state - there are no explicit units for this time measurement, it's the same units as used for the timestamps of the messages @param resultMessages an array to which midi controller-change messages will be added. This will be the minimum number of controller changes to recreate the state at the required time. */ void createControllerUpdatesForTime (int channelNumber, double time, Array& resultMessages); //============================================================================== /** Swaps this sequence with another one. */ void swapWith (MidiMessageSequence&) noexcept; private: //============================================================================== friend class MidiFile; OwnedArray list; JUCE_LEAK_DETECTOR (MidiMessageSequence) }; #endif // JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/000077500000000000000000000000001320201440200267225ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h000066400000000000000000000167101320201440200323300ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOSOURCE_H_INCLUDED #define JUCE_AUDIOSOURCE_H_INCLUDED //============================================================================== /** Used by AudioSource::getNextAudioBlock(). */ struct JUCE_API AudioSourceChannelInfo { /** Creates an uninitialised AudioSourceChannelInfo. */ AudioSourceChannelInfo() noexcept { } /** Creates an AudioSourceChannelInfo. */ AudioSourceChannelInfo (AudioSampleBuffer* bufferToUse, int startSampleOffset, int numSamplesToUse) noexcept : buffer (bufferToUse), startSample (startSampleOffset), numSamples (numSamplesToUse) { } /** Creates an AudioSourceChannelInfo that uses the whole of a buffer. Note that the buffer provided must not be deleted while the AudioSourceChannelInfo is still using it. */ explicit AudioSourceChannelInfo (AudioSampleBuffer& bufferToUse) noexcept : buffer (&bufferToUse), startSample (0), numSamples (bufferToUse.getNumSamples()) { } /** The destination buffer to fill with audio data. When the AudioSource::getNextAudioBlock() method is called, the active section of this buffer should be filled with whatever output the source produces. Only the samples specified by the startSample and numSamples members of this structure should be affected by the call. The contents of the buffer when it is passed to the AudioSource::getNextAudioBlock() method can be treated as the input if the source is performing some kind of filter operation, but should be cleared if this is not the case - the clearActiveBufferRegion() is a handy way of doing this. The number of channels in the buffer could be anything, so the AudioSource must cope with this in whatever way is appropriate for its function. */ AudioSampleBuffer* buffer; /** The first sample in the buffer from which the callback is expected to write data. */ int startSample; /** The number of samples in the buffer which the callback is expected to fill with data. */ int numSamples; /** Convenient method to clear the buffer if the source is not producing any data. */ void clearActiveBufferRegion() const { if (buffer != nullptr) buffer->clear (startSample, numSamples); } }; //============================================================================== /** Base class for objects that can produce a continuous stream of audio. An AudioSource has two states: 'prepared' and 'unprepared'. When a source needs to be played, it is first put into a 'prepared' state by a call to prepareToPlay(), and then repeated calls will be made to its getNextAudioBlock() method to process the audio data. Once playback has finished, the releaseResources() method is called to put the stream back into an 'unprepared' state. @see AudioFormatReaderSource, ResamplingAudioSource */ class JUCE_API AudioSource { protected: //============================================================================== /** Creates an AudioSource. */ AudioSource() noexcept {} public: /** Destructor. */ virtual ~AudioSource() {} //============================================================================== /** Tells the source to prepare for playing. An AudioSource has two states: prepared and unprepared. The prepareToPlay() method is guaranteed to be called at least once on an 'unpreprared' source to put it into a 'prepared' state before any calls will be made to getNextAudioBlock(). This callback allows the source to initialise any resources it might need when playing. Once playback has finished, the releaseResources() method is called to put the stream back into an 'unprepared' state. Note that this method could be called more than once in succession without a matching call to releaseResources(), so make sure your code is robust and can handle that kind of situation. @param samplesPerBlockExpected the number of samples that the source will be expected to supply each time its getNextAudioBlock() method is called. This number may vary slightly, because it will be dependent on audio hardware callbacks, and these aren't guaranteed to always use a constant block size, so the source should be able to cope with small variations. @param sampleRate the sample rate that the output will be used at - this is needed by sources such as tone generators. @see releaseResources, getNextAudioBlock */ virtual void prepareToPlay (int samplesPerBlockExpected, double sampleRate) = 0; /** Allows the source to release anything it no longer needs after playback has stopped. This will be called when the source is no longer going to have its getNextAudioBlock() method called, so it should release any spare memory, etc. that it might have allocated during the prepareToPlay() call. Note that there's no guarantee that prepareToPlay() will actually have been called before releaseResources(), and it may be called more than once in succession, so make sure your code is robust and doesn't make any assumptions about when it will be called. @see prepareToPlay, getNextAudioBlock */ virtual void releaseResources() = 0; /** Called repeatedly to fetch subsequent blocks of audio data. After calling the prepareToPlay() method, this callback will be made each time the audio playback hardware (or whatever other destination the audio data is going to) needs another block of data. It will generally be called on a high-priority system thread, or possibly even an interrupt, so be careful not to do too much work here, as that will cause audio glitches! @see AudioSourceChannelInfo, prepareToPlay, releaseResources */ virtual void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) = 0; }; #endif // JUCE_AUDIOSOURCE_H_INCLUDED juce_BufferingAudioSource.cpp000066400000000000000000000213651320201440200344360ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s, TimeSliceThread& thread, const bool deleteSourceWhenDeleted, const int bufferSizeSamples, const int numChannels) : source (s, deleteSourceWhenDeleted), backgroundThread (thread), numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)), numberOfChannels (numChannels), bufferValidStart (0), bufferValidEnd (0), nextPlayPos (0), sampleRate (0), wasSourceLooping (false), isPrepared (false) { jassert (source != nullptr); jassert (numberOfSamplesToBuffer > 1024); // not much point using this class if you're // not using a larger buffer.. } BufferingAudioSource::~BufferingAudioSource() { releaseResources(); } //============================================================================== void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate) { const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer); if (newSampleRate != sampleRate || bufferSizeNeeded != buffer.getNumSamples() || ! isPrepared) { backgroundThread.removeTimeSliceClient (this); isPrepared = true; sampleRate = newSampleRate; source->prepareToPlay (samplesPerBlockExpected, newSampleRate); buffer.setSize (numberOfChannels, bufferSizeNeeded); buffer.clear(); bufferValidStart = 0; bufferValidEnd = 0; backgroundThread.addTimeSliceClient (this); while (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2)) { backgroundThread.moveToFrontOfQueue (this); Thread::sleep (5); } } } void BufferingAudioSource::releaseResources() { isPrepared = false; backgroundThread.removeTimeSliceClient (this); buffer.setSize (numberOfChannels, 0); source->releaseResources(); } void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { const ScopedLock sl (bufferStartPosLock); const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos); const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos); if (validStart == validEnd) { // total cache miss info.clearActiveBufferRegion(); } else { if (validStart > 0) info.buffer->clear (info.startSample, validStart); // partial cache miss at start if (validEnd < info.numSamples) info.buffer->clear (info.startSample + validEnd, info.numSamples - validEnd); // partial cache miss at end if (validStart < validEnd) { for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;) { jassert (buffer.getNumSamples() > 0); const int startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples()); const int endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples()); if (startBufferIndex < endBufferIndex) { info.buffer->copyFrom (chan, info.startSample + validStart, buffer, chan, startBufferIndex, validEnd - validStart); } else { const int initialSize = buffer.getNumSamples() - startBufferIndex; info.buffer->copyFrom (chan, info.startSample + validStart, buffer, chan, startBufferIndex, initialSize); info.buffer->copyFrom (chan, info.startSample + validStart + initialSize, buffer, chan, 0, (validEnd - validStart) - initialSize); } } } nextPlayPos += info.numSamples; } } int64 BufferingAudioSource::getNextReadPosition() const { jassert (source->getTotalLength() > 0); return (source->isLooping() && nextPlayPos > 0) ? nextPlayPos % source->getTotalLength() : nextPlayPos; } void BufferingAudioSource::setNextReadPosition (int64 newPosition) { const ScopedLock sl (bufferStartPosLock); nextPlayPos = newPosition; backgroundThread.moveToFrontOfQueue (this); } bool BufferingAudioSource::readNextBufferChunk() { int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd; { const ScopedLock sl (bufferStartPosLock); if (wasSourceLooping != isLooping()) { wasSourceLooping = isLooping(); bufferValidStart = 0; bufferValidEnd = 0; } newBVS = jmax ((int64) 0, nextPlayPos); newBVE = newBVS + buffer.getNumSamples() - 4; sectionToReadStart = 0; sectionToReadEnd = 0; const int maxChunkSize = 2048; if (newBVS < bufferValidStart || newBVS >= bufferValidEnd) { newBVE = jmin (newBVE, newBVS + maxChunkSize); sectionToReadStart = newBVS; sectionToReadEnd = newBVE; bufferValidStart = 0; bufferValidEnd = 0; } else if (std::abs ((int) (newBVS - bufferValidStart)) > 512 || std::abs ((int) (newBVE - bufferValidEnd)) > 512) { newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize); sectionToReadStart = bufferValidEnd; sectionToReadEnd = newBVE; bufferValidStart = newBVS; bufferValidEnd = jmin (bufferValidEnd, newBVE); } } if (sectionToReadStart == sectionToReadEnd) return false; jassert (buffer.getNumSamples() > 0); const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples()); const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples()); if (bufferIndexStart < bufferIndexEnd) { readBufferSection (sectionToReadStart, (int) (sectionToReadEnd - sectionToReadStart), bufferIndexStart); } else { const int initialSize = buffer.getNumSamples() - bufferIndexStart; readBufferSection (sectionToReadStart, initialSize, bufferIndexStart); readBufferSection (sectionToReadStart + initialSize, (int) (sectionToReadEnd - sectionToReadStart) - initialSize, 0); } { const ScopedLock sl2 (bufferStartPosLock); bufferValidStart = newBVS; bufferValidEnd = newBVE; } return true; } void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset) { if (source->getNextReadPosition() != start) source->setNextReadPosition (start); AudioSourceChannelInfo info (&buffer, bufferOffset, length); source->getNextAudioBlock (info); } int BufferingAudioSource::useTimeSlice() { return readNextBufferChunk() ? 1 : 100; } juce_BufferingAudioSource.h000066400000000000000000000113011320201440200340700ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED #define JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED //============================================================================== /** An AudioSource which takes another source as input, and buffers it using a thread. Create this as a wrapper around another thread, and it will read-ahead with a background thread to smooth out playback. You can either create one of these directly, or use it indirectly using an AudioTransportSource. @see PositionableAudioSource, AudioTransportSource */ class JUCE_API BufferingAudioSource : public PositionableAudioSource, private TimeSliceClient { public: //============================================================================== /** Creates a BufferingAudioSource. @param source the input source to read from @param backgroundThread a background thread that will be used for the background read-ahead. This object must not be deleted until after any BufferedAudioSources that are using it have been deleted! @param deleteSourceWhenDeleted if true, then the input source object will be deleted when this object is deleted @param numberOfSamplesToBuffer the size of buffer to use for reading ahead @param numberOfChannels the number of channels that will be played */ BufferingAudioSource (PositionableAudioSource* source, TimeSliceThread& backgroundThread, bool deleteSourceWhenDeleted, int numberOfSamplesToBuffer, int numberOfChannels = 2); /** Destructor. The input source may be deleted depending on whether the deleteSourceWhenDeleted flag was set in the constructor. */ ~BufferingAudioSource(); //============================================================================== /** Implementation of the AudioSource method. */ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ void releaseResources() override; /** Implementation of the AudioSource method. */ void getNextAudioBlock (const AudioSourceChannelInfo&) override; //============================================================================== /** Implements the PositionableAudioSource method. */ void setNextReadPosition (int64 newPosition) override; /** Implements the PositionableAudioSource method. */ int64 getNextReadPosition() const override; /** Implements the PositionableAudioSource method. */ int64 getTotalLength() const override { return source->getTotalLength(); } /** Implements the PositionableAudioSource method. */ bool isLooping() const override { return source->isLooping(); } private: //============================================================================== OptionalScopedPointer source; TimeSliceThread& backgroundThread; int numberOfSamplesToBuffer, numberOfChannels; AudioSampleBuffer buffer; CriticalSection bufferStartPosLock; int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos; double volatile sampleRate; bool wasSourceLooping, isPrepared; bool readNextBufferChunk(); void readBufferSection (int64 start, int length, int bufferOffset); int useTimeSlice() override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource) }; #endif // JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED juce_ChannelRemappingAudioSource.cpp000066400000000000000000000133161320201440200357370ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ChannelRemappingAudioSource::ChannelRemappingAudioSource (AudioSource* const source_, const bool deleteSourceWhenDeleted) : source (source_, deleteSourceWhenDeleted), requiredNumberOfChannels (2) { remappedInfo.buffer = &buffer; remappedInfo.startSample = 0; } ChannelRemappingAudioSource::~ChannelRemappingAudioSource() {} //============================================================================== void ChannelRemappingAudioSource::setNumberOfChannelsToProduce (const int requiredNumberOfChannels_) { const ScopedLock sl (lock); requiredNumberOfChannels = requiredNumberOfChannels_; } void ChannelRemappingAudioSource::clearAllMappings() { const ScopedLock sl (lock); remappedInputs.clear(); remappedOutputs.clear(); } void ChannelRemappingAudioSource::setInputChannelMapping (const int destIndex, const int sourceIndex) { const ScopedLock sl (lock); while (remappedInputs.size() < destIndex) remappedInputs.add (-1); remappedInputs.set (destIndex, sourceIndex); } void ChannelRemappingAudioSource::setOutputChannelMapping (const int sourceIndex, const int destIndex) { const ScopedLock sl (lock); while (remappedOutputs.size() < sourceIndex) remappedOutputs.add (-1); remappedOutputs.set (sourceIndex, destIndex); } int ChannelRemappingAudioSource::getRemappedInputChannel (const int inputChannelIndex) const { const ScopedLock sl (lock); if (inputChannelIndex >= 0 && inputChannelIndex < remappedInputs.size()) return remappedInputs.getUnchecked (inputChannelIndex); return -1; } int ChannelRemappingAudioSource::getRemappedOutputChannel (const int outputChannelIndex) const { const ScopedLock sl (lock); if (outputChannelIndex >= 0 && outputChannelIndex < remappedOutputs.size()) return remappedOutputs .getUnchecked (outputChannelIndex); return -1; } //============================================================================== void ChannelRemappingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) { source->prepareToPlay (samplesPerBlockExpected, sampleRate); } void ChannelRemappingAudioSource::releaseResources() { source->releaseResources(); } void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) { const ScopedLock sl (lock); buffer.setSize (requiredNumberOfChannels, bufferToFill.numSamples, false, false, true); const int numChans = bufferToFill.buffer->getNumChannels(); for (int i = 0; i < buffer.getNumChannels(); ++i) { const int remappedChan = getRemappedInputChannel (i); if (remappedChan >= 0 && remappedChan < numChans) { buffer.copyFrom (i, 0, *bufferToFill.buffer, remappedChan, bufferToFill.startSample, bufferToFill.numSamples); } else { buffer.clear (i, 0, bufferToFill.numSamples); } } remappedInfo.numSamples = bufferToFill.numSamples; source->getNextAudioBlock (remappedInfo); bufferToFill.clearActiveBufferRegion(); for (int i = 0; i < requiredNumberOfChannels; ++i) { const int remappedChan = getRemappedOutputChannel (i); if (remappedChan >= 0 && remappedChan < numChans) { bufferToFill.buffer->addFrom (remappedChan, bufferToFill.startSample, buffer, i, 0, bufferToFill.numSamples); } } } //============================================================================== XmlElement* ChannelRemappingAudioSource::createXml() const { XmlElement* e = new XmlElement ("MAPPINGS"); String ins, outs; const ScopedLock sl (lock); for (int i = 0; i < remappedInputs.size(); ++i) ins << remappedInputs.getUnchecked(i) << ' '; for (int i = 0; i < remappedOutputs.size(); ++i) outs << remappedOutputs.getUnchecked(i) << ' '; e->setAttribute ("inputs", ins.trimEnd()); e->setAttribute ("outputs", outs.trimEnd()); return e; } void ChannelRemappingAudioSource::restoreFromXml (const XmlElement& e) { if (e.hasTagName ("MAPPINGS")) { const ScopedLock sl (lock); clearAllMappings(); StringArray ins, outs; ins.addTokens (e.getStringAttribute ("inputs"), false); outs.addTokens (e.getStringAttribute ("outputs"), false); for (int i = 0; i < ins.size(); ++i) remappedInputs.add (ins[i].getIntValue()); for (int i = 0; i < outs.size(); ++i) remappedOutputs.add (outs[i].getIntValue()); } } juce_ChannelRemappingAudioSource.h000066400000000000000000000137061320201440200354070ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED #define JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED //============================================================================== /** An AudioSource that takes the audio from another source, and re-maps its input and output channels to a different arrangement. You can use this to increase or decrease the number of channels that an audio source uses, or to re-order those channels. Call the reset() method before using it to set up a default mapping, and then the setInputChannelMapping() and setOutputChannelMapping() methods to create an appropriate mapping, otherwise no channels will be connected and it'll produce silence. @see AudioSource */ class ChannelRemappingAudioSource : public AudioSource { public: //============================================================================== /** Creates a remapping source that will pass on audio from the given input. @param source the input source to use. Make sure that this doesn't get deleted before the ChannelRemappingAudioSource object @param deleteSourceWhenDeleted if true, the input source will be deleted when this object is deleted, if false, the caller is responsible for its deletion */ ChannelRemappingAudioSource (AudioSource* source, bool deleteSourceWhenDeleted); /** Destructor. */ ~ChannelRemappingAudioSource(); //============================================================================== /** Specifies a number of channels that this audio source must produce from its getNextAudioBlock() callback. */ void setNumberOfChannelsToProduce (int requiredNumberOfChannels); /** Clears any mapped channels. After this, no channels are mapped, so this object will produce silence. Create some mappings with setInputChannelMapping() and setOutputChannelMapping(). */ void clearAllMappings(); /** Creates an input channel mapping. When the getNextAudioBlock() method is called, the data in channel sourceChannelIndex of the incoming data will be sent to destChannelIndex of our input source. @param destChannelIndex the index of an input channel in our input audio source (i.e. the source specified when this object was created). @param sourceChannelIndex the index of the input channel in the incoming audio data buffer during our getNextAudioBlock() callback */ void setInputChannelMapping (int destChannelIndex, int sourceChannelIndex); /** Creates an output channel mapping. When the getNextAudioBlock() method is called, the data returned in channel sourceChannelIndex by our input audio source will be copied to channel destChannelIndex of the final buffer. @param sourceChannelIndex the index of an output channel coming from our input audio source (i.e. the source specified when this object was created). @param destChannelIndex the index of the output channel in the incoming audio data buffer during our getNextAudioBlock() callback */ void setOutputChannelMapping (int sourceChannelIndex, int destChannelIndex); /** Returns the channel from our input that will be sent to channel inputChannelIndex of our input audio source. */ int getRemappedInputChannel (int inputChannelIndex) const; /** Returns the output channel to which channel outputChannelIndex of our input audio source will be sent to. */ int getRemappedOutputChannel (int outputChannelIndex) const; //============================================================================== /** Returns an XML object to encapsulate the state of the mappings. @see restoreFromXml */ XmlElement* createXml() const; /** Restores the mappings from an XML object created by createXML(). @see createXml */ void restoreFromXml (const XmlElement&); //============================================================================== void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; void releaseResources() override; void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== OptionalScopedPointer source; Array remappedInputs, remappedOutputs; int requiredNumberOfChannels; AudioSampleBuffer buffer; AudioSourceChannelInfo remappedInfo; CriticalSection lock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelRemappingAudioSource) }; #endif // JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED juce_IIRFilterAudioSource.cpp000066400000000000000000000053111320201440200343110ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ IIRFilterAudioSource::IIRFilterAudioSource (AudioSource* const inputSource, const bool deleteInputWhenDeleted) : input (inputSource, deleteInputWhenDeleted) { jassert (inputSource != nullptr); for (int i = 2; --i >= 0;) iirFilters.add (new IIRFilter()); } IIRFilterAudioSource::~IIRFilterAudioSource() {} //============================================================================== void IIRFilterAudioSource::setCoefficients (const IIRCoefficients& newCoefficients) { for (int i = iirFilters.size(); --i >= 0;) iirFilters.getUnchecked(i)->setCoefficients (newCoefficients); } void IIRFilterAudioSource::makeInactive() { for (int i = iirFilters.size(); --i >= 0;) iirFilters.getUnchecked(i)->makeInactive(); } //============================================================================== void IIRFilterAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) { input->prepareToPlay (samplesPerBlockExpected, sampleRate); for (int i = iirFilters.size(); --i >= 0;) iirFilters.getUnchecked(i)->reset(); } void IIRFilterAudioSource::releaseResources() { input->releaseResources(); } void IIRFilterAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) { input->getNextAudioBlock (bufferToFill); const int numChannels = bufferToFill.buffer->getNumChannels(); while (numChannels > iirFilters.size()) iirFilters.add (new IIRFilter (*iirFilters.getUnchecked (0))); for (int i = 0; i < numChannels; ++i) iirFilters.getUnchecked(i) ->processSamples (bufferToFill.buffer->getWritePointer (i, bufferToFill.startSample), bufferToFill.numSamples); } juce_IIRFilterAudioSource.h000066400000000000000000000053411320201440200337610ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED #define JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED //============================================================================== /** An AudioSource that performs an IIR filter on another source. */ class JUCE_API IIRFilterAudioSource : public AudioSource { public: //============================================================================== /** Creates a IIRFilterAudioSource for a given input source. @param inputSource the input source to read from - this must not be null @param deleteInputWhenDeleted if true, the input source will be deleted when this object is deleted */ IIRFilterAudioSource (AudioSource* inputSource, bool deleteInputWhenDeleted); /** Destructor. */ ~IIRFilterAudioSource(); //============================================================================== /** Changes the filter to use the same parameters as the one being passed in. */ void setCoefficients (const IIRCoefficients& newCoefficients); /** Calls IIRFilter::makeInactive() on all the filters being used internally. */ void makeInactive(); //============================================================================== void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; void releaseResources() override; void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== OptionalScopedPointer input; OwnedArray iirFilters; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IIRFilterAudioSource) }; #endif // JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp000066400000000000000000000103701320201440200336640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ MixerAudioSource::MixerAudioSource() : currentSampleRate (0.0), bufferSizeExpected (0) { } MixerAudioSource::~MixerAudioSource() { removeAllInputs(); } //============================================================================== void MixerAudioSource::addInputSource (AudioSource* input, const bool deleteWhenRemoved) { if (input != nullptr && ! inputs.contains (input)) { double localRate; int localBufferSize; { const ScopedLock sl (lock); localRate = currentSampleRate; localBufferSize = bufferSizeExpected; } if (localRate > 0.0) input->prepareToPlay (localBufferSize, localRate); const ScopedLock sl (lock); inputsToDelete.setBit (inputs.size(), deleteWhenRemoved); inputs.add (input); } } void MixerAudioSource::removeInputSource (AudioSource* const input) { if (input != nullptr) { ScopedPointer toDelete; { const ScopedLock sl (lock); const int index = inputs.indexOf (input); if (index < 0) return; if (inputsToDelete [index]) toDelete = input; inputsToDelete.shiftBits (-1, index); inputs.remove (index); } input->releaseResources(); } } void MixerAudioSource::removeAllInputs() { OwnedArray toDelete; { const ScopedLock sl (lock); for (int i = inputs.size(); --i >= 0;) if (inputsToDelete[i]) toDelete.add (inputs.getUnchecked(i)); inputs.clear(); } for (int i = toDelete.size(); --i >= 0;) toDelete.getUnchecked(i)->releaseResources(); } void MixerAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) { tempBuffer.setSize (2, samplesPerBlockExpected); const ScopedLock sl (lock); currentSampleRate = sampleRate; bufferSizeExpected = samplesPerBlockExpected; for (int i = inputs.size(); --i >= 0;) inputs.getUnchecked(i)->prepareToPlay (samplesPerBlockExpected, sampleRate); } void MixerAudioSource::releaseResources() { const ScopedLock sl (lock); for (int i = inputs.size(); --i >= 0;) inputs.getUnchecked(i)->releaseResources(); tempBuffer.setSize (2, 0); currentSampleRate = 0; bufferSizeExpected = 0; } void MixerAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { const ScopedLock sl (lock); if (inputs.size() > 0) { inputs.getUnchecked(0)->getNextAudioBlock (info); if (inputs.size() > 1) { tempBuffer.setSize (jmax (1, info.buffer->getNumChannels()), info.buffer->getNumSamples()); AudioSourceChannelInfo info2 (&tempBuffer, 0, info.numSamples); for (int i = 1; i < inputs.size(); ++i) { inputs.getUnchecked(i)->getNextAudioBlock (info2); for (int chan = 0; chan < info.buffer->getNumChannels(); ++chan) info.buffer->addFrom (chan, info.startSample, tempBuffer, chan, 0, info.numSamples); } } } else { info.clearActiveBufferRegion(); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h000066400000000000000000000074421320201440200333370ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIXERAUDIOSOURCE_H_INCLUDED #define JUCE_MIXERAUDIOSOURCE_H_INCLUDED //============================================================================== /** An AudioSource that mixes together the output of a set of other AudioSources. Input sources can be added and removed while the mixer is running as long as their prepareToPlay() and releaseResources() methods are called before and after adding them to the mixer. */ class JUCE_API MixerAudioSource : public AudioSource { public: //============================================================================== /** Creates a MixerAudioSource. */ MixerAudioSource(); /** Destructor. */ ~MixerAudioSource(); //============================================================================== /** Adds an input source to the mixer. If the mixer is running you'll need to make sure that the input source is ready to play by calling its prepareToPlay() method before adding it. If the mixer is stopped, then its input sources will be automatically prepared when the mixer's prepareToPlay() method is called. @param newInput the source to add to the mixer @param deleteWhenRemoved if true, then this source will be deleted when no longer needed by the mixer. */ void addInputSource (AudioSource* newInput, bool deleteWhenRemoved); /** Removes an input source. If the source was added by calling addInputSource() with the deleteWhenRemoved flag set, it will be deleted by this method. */ void removeInputSource (AudioSource* input); /** Removes all the input sources. Any sources which were added by calling addInputSource() with the deleteWhenRemoved flag set will be deleted by this method. */ void removeAllInputs(); //============================================================================== /** Implementation of the AudioSource method. This will call prepareToPlay() on all its input sources. */ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. This will call releaseResources() on all its input sources. */ void releaseResources() override; /** Implementation of the AudioSource method. */ void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== Array inputs; BigInteger inputsToDelete; CriticalSection lock; AudioSampleBuffer tempBuffer; double currentSampleRate; int bufferSizeExpected; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MixerAudioSource) }; #endif // JUCE_MIXERAUDIOSOURCE_H_INCLUDED juce_PositionableAudioSource.h000066400000000000000000000054631320201440200346250ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED #define JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED //============================================================================== /** A type of AudioSource which can be repositioned. The basic AudioSource just streams continuously with no idea of a current time or length, so the PositionableAudioSource is used for a finite stream that has a current read position. @see AudioSource, AudioTransportSource */ class JUCE_API PositionableAudioSource : public AudioSource { protected: //============================================================================== /** Creates the PositionableAudioSource. */ PositionableAudioSource() noexcept {} public: /** Destructor */ ~PositionableAudioSource() {} //============================================================================== /** Tells the stream to move to a new position. Calling this indicates that the next call to AudioSource::getNextAudioBlock() should return samples from this position. Note that this may be called on a different thread to getNextAudioBlock(), so the subclass should make sure it's synchronised. */ virtual void setNextReadPosition (int64 newPosition) = 0; /** Returns the position from which the next block will be returned. @see setNextReadPosition */ virtual int64 getNextReadPosition() const = 0; /** Returns the total length of the stream (in samples). */ virtual int64 getTotalLength() const = 0; /** Returns true if this source is actually playing in a loop. */ virtual bool isLooping() const = 0; /** Tells the source whether you'd like it to play in a loop. */ virtual void setLooping (bool shouldLoop) { (void) shouldLoop; } }; #endif // JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED juce_ResamplingAudioSource.cpp000066400000000000000000000176251320201440200346340ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, const bool deleteInputWhenDeleted, const int channels) : input (inputSource, deleteInputWhenDeleted), ratio (1.0), lastRatio (1.0), bufferPos (0), sampsInBuffer (0), subSampleOffset (0), numChannels (channels) { jassert (input != nullptr); zeromem (coefficients, sizeof (coefficients)); } ResamplingAudioSource::~ResamplingAudioSource() {} void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample) { jassert (samplesInPerOutputSample > 0); const SpinLock::ScopedLockType sl (ratioLock); ratio = jmax (0.0, samplesInPerOutputSample); } void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) { const SpinLock::ScopedLockType sl (ratioLock); const int scaledBlockSize = roundToInt (samplesPerBlockExpected * ratio); input->prepareToPlay (scaledBlockSize, sampleRate * ratio); buffer.setSize (numChannels, scaledBlockSize + 32); filterStates.calloc ((size_t) numChannels); srcBuffers.calloc ((size_t) numChannels); destBuffers.calloc ((size_t) numChannels); createLowPass (ratio); flushBuffers(); } void ResamplingAudioSource::flushBuffers() { buffer.clear(); bufferPos = 0; sampsInBuffer = 0; subSampleOffset = 0.0; resetFilters(); } void ResamplingAudioSource::releaseResources() { input->releaseResources(); buffer.setSize (numChannels, 0); } void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { double localRatio; { const SpinLock::ScopedLockType sl (ratioLock); localRatio = ratio; } if (lastRatio != localRatio) { createLowPass (localRatio); lastRatio = localRatio; } const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 3; int bufferSize = buffer.getNumSamples(); if (bufferSize < sampsNeeded + 8) { bufferPos %= bufferSize; bufferSize = sampsNeeded + 32; buffer.setSize (buffer.getNumChannels(), bufferSize, true, true); } bufferPos %= bufferSize; int endOfBufferPos = bufferPos + sampsInBuffer; const int channelsToProcess = jmin (numChannels, info.buffer->getNumChannels()); while (sampsNeeded > sampsInBuffer) { endOfBufferPos %= bufferSize; int numToDo = jmin (sampsNeeded - sampsInBuffer, bufferSize - endOfBufferPos); AudioSourceChannelInfo readInfo (&buffer, endOfBufferPos, numToDo); input->getNextAudioBlock (readInfo); if (localRatio > 1.0001) { // for down-sampling, pre-apply the filter.. for (int i = channelsToProcess; --i >= 0;) applyFilter (buffer.getWritePointer (i, endOfBufferPos), numToDo, filterStates[i]); } sampsInBuffer += numToDo; endOfBufferPos += numToDo; } for (int channel = 0; channel < channelsToProcess; ++channel) { destBuffers[channel] = info.buffer->getWritePointer (channel, info.startSample); srcBuffers[channel] = buffer.getReadPointer (channel); } int nextPos = (bufferPos + 1) % bufferSize; for (int m = info.numSamples; --m >= 0;) { jassert (sampsInBuffer > 0 && nextPos != endOfBufferPos); const float alpha = (float) subSampleOffset; for (int channel = 0; channel < channelsToProcess; ++channel) *destBuffers[channel]++ = srcBuffers[channel][bufferPos] + alpha * (srcBuffers[channel][nextPos] - srcBuffers[channel][bufferPos]); subSampleOffset += localRatio; while (subSampleOffset >= 1.0) { if (++bufferPos >= bufferSize) bufferPos = 0; --sampsInBuffer; nextPos = (bufferPos + 1) % bufferSize; subSampleOffset -= 1.0; } } if (localRatio < 0.9999) { // for up-sampling, apply the filter after transposing.. for (int i = channelsToProcess; --i >= 0;) applyFilter (info.buffer->getWritePointer (i, info.startSample), info.numSamples, filterStates[i]); } else if (localRatio <= 1.0001 && info.numSamples > 0) { // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities for (int i = channelsToProcess; --i >= 0;) { const float* const endOfBuffer = info.buffer->getReadPointer (i, info.startSample + info.numSamples - 1); FilterState& fs = filterStates[i]; if (info.numSamples > 1) { fs.y2 = fs.x2 = *(endOfBuffer - 1); } else { fs.y2 = fs.y1; fs.x2 = fs.x1; } fs.y1 = fs.x1 = *endOfBuffer; } } jassert (sampsInBuffer >= 0); } void ResamplingAudioSource::createLowPass (const double frequencyRatio) { const double proportionalRate = (frequencyRatio > 1.0) ? 0.5 / frequencyRatio : 0.5 * frequencyRatio; const double n = 1.0 / std::tan (double_Pi * jmax (0.001, proportionalRate)); const double nSquared = n * n; const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); setFilterCoefficients (c1, c1 * 2.0f, c1, 1.0, c1 * 2.0 * (1.0 - nSquared), c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) { const double a = 1.0 / c4; c1 *= a; c2 *= a; c3 *= a; c5 *= a; c6 *= a; coefficients[0] = c1; coefficients[1] = c2; coefficients[2] = c3; coefficients[3] = c4; coefficients[4] = c5; coefficients[5] = c6; } void ResamplingAudioSource::resetFilters() { if (filterStates != nullptr) filterStates.clear ((size_t) numChannels); } void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) { while (--num >= 0) { const double in = *samples; double out = coefficients[0] * in + coefficients[1] * fs.x1 + coefficients[2] * fs.x2 - coefficients[4] * fs.y1 - coefficients[5] * fs.y2; #if JUCE_INTEL if (! (out < -1.0e-8 || out > 1.0e-8)) out = 0; #endif fs.x2 = fs.x1; fs.x1 = in; fs.y2 = fs.y1; fs.y1 = out; *samples++ = (float) out; } } juce_ResamplingAudioSource.h000066400000000000000000000076031320201440200342740ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED #define JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED //============================================================================== /** A type of AudioSource that takes an input source and changes its sample rate. @see AudioSource */ class JUCE_API ResamplingAudioSource : public AudioSource { public: //============================================================================== /** Creates a ResamplingAudioSource for a given input source. @param inputSource the input source to read from @param deleteInputWhenDeleted if true, the input source will be deleted when this object is deleted @param numChannels the number of channels to process */ ResamplingAudioSource (AudioSource* inputSource, bool deleteInputWhenDeleted, int numChannels = 2); /** Destructor. */ ~ResamplingAudioSource(); /** Changes the resampling ratio. (This value can be changed at any time, even while the source is running). @param samplesInPerOutputSample if set to 1.0, the input is passed through; higher values will speed it up; lower values will slow it down. The ratio must be greater than 0 */ void setResamplingRatio (double samplesInPerOutputSample); /** Returns the current resampling ratio. This is the value that was set by setResamplingRatio(). */ double getResamplingRatio() const noexcept { return ratio; } /** Clears any buffers and filters that the resampler is using. */ void flushBuffers(); //============================================================================== void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; void releaseResources() override; void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== OptionalScopedPointer input; double ratio, lastRatio; AudioSampleBuffer buffer; int bufferPos, sampsInBuffer; double subSampleOffset; double coefficients[6]; SpinLock ratioLock; const int numChannels; HeapBlock destBuffers; HeapBlock srcBuffers; void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); void createLowPass (double proportionalRate); struct FilterState { double x1, x2, y1, y2; }; HeapBlock filterStates; void resetFilters(); void applyFilter (float* samples, int num, FilterState& fs); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResamplingAudioSource) }; #endif // JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp000066400000000000000000000047551320201440200340370ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ReverbAudioSource::ReverbAudioSource (AudioSource* const inputSource, const bool deleteInputWhenDeleted) : input (inputSource, deleteInputWhenDeleted), bypass (false) { jassert (inputSource != nullptr); } ReverbAudioSource::~ReverbAudioSource() {} void ReverbAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) { const ScopedLock sl (lock); input->prepareToPlay (samplesPerBlockExpected, sampleRate); reverb.setSampleRate (sampleRate); } void ReverbAudioSource::releaseResources() {} void ReverbAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) { const ScopedLock sl (lock); input->getNextAudioBlock (bufferToFill); if (! bypass) { float* const firstChannel = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); if (bufferToFill.buffer->getNumChannels() > 1) { reverb.processStereo (firstChannel, bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample), bufferToFill.numSamples); } else { reverb.processMono (firstChannel, bufferToFill.numSamples); } } } void ReverbAudioSource::setParameters (const Reverb::Parameters& newParams) { const ScopedLock sl (lock); reverb.setParameters (newParams); } void ReverbAudioSource::setBypassed (bool b) noexcept { if (bypass != b) { const ScopedLock sl (lock); bypass = b; reverb.reset(); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h000066400000000000000000000055061320201440200334770ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_REVERBAUDIOSOURCE_H_INCLUDED #define JUCE_REVERBAUDIOSOURCE_H_INCLUDED //============================================================================== /** An AudioSource that uses the Reverb class to apply a reverb to another AudioSource. @see Reverb */ class JUCE_API ReverbAudioSource : public AudioSource { public: /** Creates a ReverbAudioSource to process a given input source. @param inputSource the input source to read from - this must not be null @param deleteInputWhenDeleted if true, the input source will be deleted when this object is deleted */ ReverbAudioSource (AudioSource* inputSource, bool deleteInputWhenDeleted); /** Destructor. */ ~ReverbAudioSource(); //============================================================================== /** Returns the parameters from the reverb. */ const Reverb::Parameters& getParameters() const noexcept { return reverb.getParameters(); } /** Changes the reverb's parameters. */ void setParameters (const Reverb::Parameters& newParams); void setBypassed (bool isBypassed) noexcept; bool isBypassed() const noexcept { return bypass; } //============================================================================== void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; void releaseResources() override; void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== CriticalSection lock; OptionalScopedPointer input; Reverb reverb; volatile bool bypass; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbAudioSource) }; #endif // JUCE_REVERBAUDIOSOURCE_H_INCLUDED juce_ToneGeneratorAudioSource.cpp000066400000000000000000000044651320201440200353050ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ToneGeneratorAudioSource::ToneGeneratorAudioSource() : frequency (1000.0), sampleRate (44100.0), currentPhase (0.0), phasePerSample (0.0), amplitude (0.5f) { } ToneGeneratorAudioSource::~ToneGeneratorAudioSource() { } //============================================================================== void ToneGeneratorAudioSource::setAmplitude (const float newAmplitude) { amplitude = newAmplitude; } void ToneGeneratorAudioSource::setFrequency (const double newFrequencyHz) { frequency = newFrequencyHz; phasePerSample = 0.0; } //============================================================================== void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, double rate) { currentPhase = 0.0; phasePerSample = 0.0; sampleRate = rate; } void ToneGeneratorAudioSource::releaseResources() { } void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { if (phasePerSample == 0.0) phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); for (int i = 0; i < info.numSamples; ++i) { const float sample = amplitude * (float) std::sin (currentPhase); currentPhase += phasePerSample; for (int j = info.buffer->getNumChannels(); --j >= 0;) info.buffer->setSample (j, info.startSample + i, sample); } } juce_ToneGeneratorAudioSource.h000066400000000000000000000047521320201440200347510ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED #define JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED //============================================================================== /** A simple AudioSource that generates a sine wave. */ class JUCE_API ToneGeneratorAudioSource : public AudioSource { public: //============================================================================== /** Creates a ToneGeneratorAudioSource. */ ToneGeneratorAudioSource(); /** Destructor. */ ~ToneGeneratorAudioSource(); //============================================================================== /** Sets the signal's amplitude. */ void setAmplitude (float newAmplitude); /** Sets the signal's frequency. */ void setFrequency (double newFrequencyHz); //============================================================================== /** Implementation of the AudioSource method. */ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ void releaseResources() override; /** Implementation of the AudioSource method. */ void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== double frequency, sampleRate; double currentPhase, phasePerSample; float amplitude; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToneGeneratorAudioSource) }; #endif // JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/synthesisers/000077500000000000000000000000001320201440200300025ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp000066400000000000000000000454351320201440200340470ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ SynthesiserSound::SynthesiserSound() {} SynthesiserSound::~SynthesiserSound() {} //============================================================================== SynthesiserVoice::SynthesiserVoice() : currentSampleRate (44100.0), currentlyPlayingNote (-1), currentPlayingMidiChannel (0), noteOnTime (0), keyIsDown (false), sustainPedalDown (false), sostenutoPedalDown (false) { } SynthesiserVoice::~SynthesiserVoice() { } bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const { return currentPlayingMidiChannel == midiChannel; } void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate) { currentSampleRate = newRate; } bool SynthesiserVoice::isVoiceActive() const { return getCurrentlyPlayingNote() >= 0; } void SynthesiserVoice::clearCurrentNote() { currentlyPlayingNote = -1; currentlyPlayingSound = nullptr; currentPlayingMidiChannel = 0; } void SynthesiserVoice::aftertouchChanged (int) {} void SynthesiserVoice::channelPressureChanged (int) {} bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept { return noteOnTime < other.noteOnTime; } //============================================================================== Synthesiser::Synthesiser() : sampleRate (0), lastNoteOnCounter (0), minimumSubBlockSize (32), shouldStealNotes (true) { for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) lastPitchWheelValues[i] = 0x2000; } Synthesiser::~Synthesiser() { } //============================================================================== SynthesiserVoice* Synthesiser::getVoice (const int index) const { const ScopedLock sl (lock); return voices [index]; } void Synthesiser::clearVoices() { const ScopedLock sl (lock); voices.clear(); } SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice) { const ScopedLock sl (lock); return voices.add (newVoice); } void Synthesiser::removeVoice (const int index) { const ScopedLock sl (lock); voices.remove (index); } void Synthesiser::clearSounds() { const ScopedLock sl (lock); sounds.clear(); } SynthesiserSound* Synthesiser::addSound (const SynthesiserSound::Ptr& newSound) { const ScopedLock sl (lock); return sounds.add (newSound); } void Synthesiser::removeSound (const int index) { const ScopedLock sl (lock); sounds.remove (index); } void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) { shouldStealNotes = shouldSteal; } void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples) noexcept { jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 minimumSubBlockSize = numSamples; } //============================================================================== void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) { if (sampleRate != newRate) { const ScopedLock sl (lock); allNotesOff (0, false); sampleRate = newRate; for (int i = voices.size(); --i >= 0;) voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate); } } void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBuffer& midiData, int startSample, int numSamples) { // must set the sample rate before using this! jassert (sampleRate != 0); MidiBuffer::Iterator midiIterator (midiData); midiIterator.setNextSamplePosition (startSample); int midiEventPos; MidiMessage m; const ScopedLock sl (lock); while (numSamples > 0) { if (! midiIterator.getNextEvent (m, midiEventPos)) { renderVoices (outputBuffer, startSample, numSamples); return; } const int samplesToNextMidiMessage = midiEventPos - startSample; if (samplesToNextMidiMessage >= numSamples) { renderVoices (outputBuffer, startSample, numSamples); handleMidiEvent (m); break; } if (samplesToNextMidiMessage < minimumSubBlockSize) { handleMidiEvent (m); continue; } renderVoices (outputBuffer, startSample, samplesToNextMidiMessage); handleMidiEvent (m); startSample += samplesToNextMidiMessage; numSamples -= samplesToNextMidiMessage; } while (midiIterator.getNextEvent (m, midiEventPos)) handleMidiEvent (m); } void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples) { for (int i = voices.size(); --i >= 0;) voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples); } void Synthesiser::handleMidiEvent (const MidiMessage& m) { const int channel = m.getChannel(); if (m.isNoteOn()) { noteOn (channel, m.getNoteNumber(), m.getFloatVelocity()); } else if (m.isNoteOff()) { noteOff (channel, m.getNoteNumber(), m.getFloatVelocity(), true); } else if (m.isAllNotesOff() || m.isAllSoundOff()) { allNotesOff (channel, true); } else if (m.isPitchWheel()) { const int wheelPos = m.getPitchWheelValue(); lastPitchWheelValues [channel - 1] = wheelPos; handlePitchWheel (channel, wheelPos); } else if (m.isAftertouch()) { handleAftertouch (channel, m.getNoteNumber(), m.getAfterTouchValue()); } else if (m.isChannelPressure()) { handleChannelPressure (channel, m.getChannelPressureValue()); } else if (m.isController()) { handleController (channel, m.getControllerNumber(), m.getControllerValue()); } else if (m.isProgramChange()) { handleProgramChange (channel, m.getProgramChangeNumber()); } } //============================================================================== void Synthesiser::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity) { const ScopedLock sl (lock); for (int i = sounds.size(); --i >= 0;) { SynthesiserSound* const sound = sounds.getUnchecked(i); if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel)) { // If hitting a note that's still ringing, stop it first (it could be // still playing because of the sustain or sostenuto pedal). for (int j = voices.size(); --j >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (j); if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel)) stopVoice (voice, 1.0f, true); } startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes), sound, midiChannel, midiNoteNumber, velocity); } } } void Synthesiser::startVoice (SynthesiserVoice* const voice, SynthesiserSound* const sound, const int midiChannel, const int midiNoteNumber, const float velocity) { if (voice != nullptr && sound != nullptr) { if (voice->currentlyPlayingSound != nullptr) voice->stopNote (0.0f, false); voice->currentlyPlayingNote = midiNoteNumber; voice->currentPlayingMidiChannel = midiChannel; voice->noteOnTime = ++lastNoteOnCounter; voice->currentlyPlayingSound = sound; voice->keyIsDown = true; voice->sostenutoPedalDown = false; voice->sustainPedalDown = sustainPedalsDown[midiChannel]; voice->startNote (midiNoteNumber, velocity, sound, lastPitchWheelValues [midiChannel - 1]); } } void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff) { jassert (voice != nullptr); voice->stopNote (velocity, allowTailOff); // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0)); } void Synthesiser::noteOff (const int midiChannel, const int midiNoteNumber, const float velocity, const bool allowTailOff) { const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel)) { if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound()) { if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel)) { jassert (! voice->keyIsDown || voice->sustainPedalDown == sustainPedalsDown [midiChannel]); voice->keyIsDown = false; if (! (voice->sustainPedalDown || voice->sostenutoPedalDown)) stopVoice (voice, velocity, allowTailOff); } } } } } void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff) { const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->stopNote (1.0f, allowTailOff); } sustainPedalsDown.clear(); } void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue) { const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->pitchWheelMoved (wheelValue); } } void Synthesiser::handleController (const int midiChannel, const int controllerNumber, const int controllerValue) { switch (controllerNumber) { case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break; case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break; case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break; default: break; } const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->controllerMoved (controllerNumber, controllerValue); } } void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue) { const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->getCurrentlyPlayingNote() == midiNoteNumber && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))) voice->aftertouchChanged (aftertouchValue); } } void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue) { const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->channelPressureChanged (channelPressureValue); } } void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) { jassert (midiChannel > 0 && midiChannel <= 16); const ScopedLock sl (lock); if (isDown) { sustainPedalsDown.setBit (midiChannel); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown()) voice->sustainPedalDown = true; } } else { for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->isPlayingChannel (midiChannel)) { voice->sustainPedalDown = false; if (! voice->isKeyDown()) stopVoice (voice, 1.0f, true); } } sustainPedalsDown.clearBit (midiChannel); } } void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) { jassert (midiChannel > 0 && midiChannel <= 16); const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->isPlayingChannel (midiChannel)) { if (isDown) voice->sostenutoPedalDown = true; else if (voice->sostenutoPedalDown) stopVoice (voice, 1.0f, true); } } } void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) { (void) midiChannel; jassert (midiChannel > 0 && midiChannel <= 16); } void Synthesiser::handleProgramChange (int midiChannel, int programNumber) { (void) midiChannel; (void) programNumber; jassert (midiChannel > 0 && midiChannel <= 16); } //============================================================================== SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, int midiChannel, int midiNoteNumber, const bool stealIfNoneAvailable) const { const ScopedLock sl (lock); for (int i = 0; i < voices.size(); ++i) { SynthesiserVoice* const voice = voices.getUnchecked (i); if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay)) return voice; } if (stealIfNoneAvailable) return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber); return nullptr; } struct VoiceAgeSorter { static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept { return v1->wasStartedBefore (*v2) ? -1 : (v2->wasStartedBefore (*v1) ? 1 : 0); } }; SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, int /*midiChannel*/, int midiNoteNumber) const { // This voice-stealing algorithm applies the following heuristics: // - Re-use the oldest notes first // - Protect the lowest & topmost notes, even if sustained, but not if they've been released. // These are the voices we want to protect (ie: only steal if unavoidable) SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase // this is a list of voices we can steal, sorted by how long they've been running Array usableVoices; usableVoices.ensureStorageAllocated (voices.size()); for (int i = 0; i < voices.size(); ++i) { SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->canPlaySound (soundToPlay)) { jassert (voice->isVoiceActive()); // We wouldn't be here otherwise VoiceAgeSorter sorter; usableVoices.addSorted (sorter, voice); if (! voice->isPlayingButReleased()) // Don't protect released notes { const int note = voice->getCurrentlyPlayingNote(); if (low == nullptr || note < low->getCurrentlyPlayingNote()) low = voice; if (top == nullptr || note > top->getCurrentlyPlayingNote()) top = voice; } } } // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s) if (top == low) top = nullptr; const int numUsableVoices = usableVoices.size(); // The oldest note that's playing with the target pitch is ideal.. for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); if (voice->getCurrentlyPlayingNote() == midiNoteNumber) return voice; } // Oldest voice that has been released (no finger on it and not held by sustain pedal) for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); if (voice != low && voice != top && voice->isPlayingButReleased()) return voice; } // Oldest voice that doesn't have a finger on it: for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); if (voice != low && voice != top && ! voice->isKeyDown()) return voice; } // Oldest voice that isn't protected for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); if (voice != low && voice != top) return voice; } // We've only got "protected" voices now: lowest note takes priority jassert (low != nullptr); // Duophonic synth: give priority to the bass note: if (top != nullptr) return top; return low; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h000066400000000000000000000644011320201440200335060ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_SYNTHESISER_H_INCLUDED #define JUCE_SYNTHESISER_H_INCLUDED //============================================================================== /** Describes one of the sounds that a Synthesiser can play. A synthesiser can contain one or more sounds, and a sound can choose which midi notes and channels can trigger it. The SynthesiserSound is a passive class that just describes what the sound is - the actual audio rendering for a sound is done by a SynthesiserVoice. This allows more than one SynthesiserVoice to play the same sound at the same time. @see Synthesiser, SynthesiserVoice */ class JUCE_API SynthesiserSound : public ReferenceCountedObject { protected: //============================================================================== SynthesiserSound(); public: /** Destructor. */ virtual ~SynthesiserSound(); //============================================================================== /** Returns true if this sound should be played when a given midi note is pressed. The Synthesiser will use this information when deciding which sounds to trigger for a given note. */ virtual bool appliesToNote (int midiNoteNumber) = 0; /** Returns true if the sound should be triggered by midi events on a given channel. The Synthesiser will use this information when deciding which sounds to trigger for a given note. */ virtual bool appliesToChannel (int midiChannel) = 0; /** The class is reference-counted, so this is a handy pointer class for it. */ typedef ReferenceCountedObjectPtr Ptr; private: //============================================================================== JUCE_LEAK_DETECTOR (SynthesiserSound) }; //============================================================================== /** Represents a voice that a Synthesiser can use to play a SynthesiserSound. A voice plays a single sound at a time, and a synthesiser holds an array of voices so that it can play polyphonically. @see Synthesiser, SynthesiserSound */ class JUCE_API SynthesiserVoice { public: //============================================================================== /** Creates a voice. */ SynthesiserVoice(); /** Destructor. */ virtual ~SynthesiserVoice(); //============================================================================== /** Returns the midi note that this voice is currently playing. Returns a value less than 0 if no note is playing. */ int getCurrentlyPlayingNote() const noexcept { return currentlyPlayingNote; } /** Returns the sound that this voice is currently playing. Returns nullptr if it's not playing. */ SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept { return currentlyPlayingSound; } /** Must return true if this voice object is capable of playing the given sound. If there are different classes of sound, and different classes of voice, a voice can choose which ones it wants to take on. A typical implementation of this method may just return true if there's only one type of voice and sound, or it might check the type of the sound object passed-in and see if it's one that it understands. */ virtual bool canPlaySound (SynthesiserSound*) = 0; /** Called to start a new note. This will be called during the rendering callback, so must be fast and thread-safe. */ virtual void startNote (int midiNoteNumber, float velocity, SynthesiserSound* sound, int currentPitchWheelPosition) = 0; /** Called to stop a note. This will be called during the rendering callback, so must be fast and thread-safe. The velocity indicates how quickly the note was released - 0 is slowly, 1 is quickly. If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all sound immediately, and must call clearCurrentNote() to reset the state of this voice and allow the synth to reassign it another sound. If allowTailOff is true and the voice decides to do a tail-off, then it's allowed to begin fading out its sound, and it can stop playing until it's finished. As soon as it finishes playing (during the rendering callback), it must make sure that it calls clearCurrentNote(). */ virtual void stopNote (float velocity, bool allowTailOff) = 0; /** Returns true if this voice is currently busy playing a sound. By default this just checks the getCurrentlyPlayingNote() value, but can be overridden for more advanced checking. */ virtual bool isVoiceActive() const; /** Called to let the voice know that the pitch wheel has been moved. This will be called during the rendering callback, so must be fast and thread-safe. */ virtual void pitchWheelMoved (int newPitchWheelValue) = 0; /** Called to let the voice know that a midi controller has been moved. This will be called during the rendering callback, so must be fast and thread-safe. */ virtual void controllerMoved (int controllerNumber, int newControllerValue) = 0; /** Called to let the voice know that the aftertouch has changed. This will be called during the rendering callback, so must be fast and thread-safe. */ virtual void aftertouchChanged (int newAftertouchValue); /** Called to let the voice know that the channel pressure has changed. This will be called during the rendering callback, so must be fast and thread-safe. */ virtual void channelPressureChanged (int newChannelPressureValue); //============================================================================== /** Renders the next block of data for this voice. The output audio data must be added to the current contents of the buffer provided. Only the region of the buffer between startSample and (startSample + numSamples) should be altered by this method. If the voice is currently silent, it should just return without doing anything. If the sound that the voice is playing finishes during the course of this rendered block, it must call clearCurrentNote(), to tell the synthesiser that it has finished. The size of the blocks that are rendered can change each time it is called, and may involve rendering as little as 1 sample at a time. In between rendering callbacks, the voice's methods will be called to tell it about note and controller events. */ virtual void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) = 0; /** Changes the voice's reference sample rate. The rate is set so that subclasses know the output rate and can set their pitch accordingly. This method is called by the synth, and subclasses can access the current rate with the currentSampleRate member. */ virtual void setCurrentPlaybackSampleRate (double newRate); /** Returns true if the voice is currently playing a sound which is mapped to the given midi channel. If it's not currently playing, this will return false. */ virtual bool isPlayingChannel (int midiChannel) const; /** Returns the current target sample rate at which rendering is being done. Subclasses may need to know this so that they can pitch things correctly. */ double getSampleRate() const noexcept { return currentSampleRate; } /** Returns true if the key that triggered this voice is still held down. Note that the voice may still be playing after the key was released (e.g because the sostenuto pedal is down). */ bool isKeyDown() const noexcept { return keyIsDown; } /** Returns true if the sustain pedal is currently active for this voice. */ bool isSustainPedalDown() const noexcept { return sustainPedalDown; } /** Returns true if the sostenuto pedal is currently active for this voice. */ bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } /** Returns true if a voice is sounding in its release phase **/ bool isPlayingButReleased() const noexcept { return isVoiceActive() && ! (isKeyDown() || isSostenutoPedalDown() || isSustainPedalDown()); } /** Returns true if this voice started playing its current note before the other voice did. */ bool wasStartedBefore (const SynthesiserVoice& other) const noexcept; protected: /** Resets the state of this voice after a sound has finished playing. The subclass must call this when it finishes playing a note and becomes available to play new ones. It must either call it in the stopNote() method, or if the voice is tailing off, then it should call it later during the renderNextBlock method, as soon as it finishes its tail-off. It can also be called at any time during the render callback if the sound happens to have finished, e.g. if it's playing a sample and the sample finishes. */ void clearCurrentNote(); private: //============================================================================== friend class Synthesiser; double currentSampleRate; int currentlyPlayingNote, currentPlayingMidiChannel; uint32 noteOnTime; SynthesiserSound::Ptr currentlyPlayingSound; bool keyIsDown, sustainPedalDown, sostenutoPedalDown; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // Note the new parameters for this method. virtual int stopNote (bool) { return 0; } #endif JUCE_LEAK_DETECTOR (SynthesiserVoice) }; //============================================================================== /** Base class for a musical device that can play sounds. To create a synthesiser, you'll need to create a subclass of SynthesiserSound to describe each sound available to your synth, and a subclass of SynthesiserVoice which can play back one of these sounds. Then you can use the addVoice() and addSound() methods to give the synthesiser a set of sounds, and a set of voices it can use to play them. If you only give it one voice it will be monophonic - the more voices it has, the more polyphony it'll have available. Then repeatedly call the renderNextBlock() method to produce the audio. Any midi events that go in will be scanned for note on/off messages, and these are used to start and stop the voices playing the appropriate sounds. While it's playing, you can also cause notes to be triggered by calling the noteOn(), noteOff() and other controller methods. Before rendering, be sure to call the setCurrentPlaybackSampleRate() to tell it what the target playback rate is. This value is passed on to the voices so that they can pitch their output correctly. */ class JUCE_API Synthesiser { public: //============================================================================== /** Creates a new synthesiser. You'll need to add some sounds and voices before it'll make any sound. */ Synthesiser(); /** Destructor. */ virtual ~Synthesiser(); //============================================================================== /** Deletes all voices. */ void clearVoices(); /** Returns the number of voices that have been added. */ int getNumVoices() const noexcept { return voices.size(); } /** Returns one of the voices that have been added. */ SynthesiserVoice* getVoice (int index) const; /** Adds a new voice to the synth. All the voices should be the same class of object and are treated equally. The object passed in will be managed by the synthesiser, which will delete it later on when no longer needed. The caller should not retain a pointer to the voice. */ SynthesiserVoice* addVoice (SynthesiserVoice* newVoice); /** Deletes one of the voices. */ void removeVoice (int index); //============================================================================== /** Deletes all sounds. */ void clearSounds(); /** Returns the number of sounds that have been added to the synth. */ int getNumSounds() const noexcept { return sounds.size(); } /** Returns one of the sounds. */ SynthesiserSound* getSound (int index) const noexcept { return sounds [index]; } /** Adds a new sound to the synthesiser. The object passed in is reference counted, so will be deleted when the synthesiser and all voices are no longer using it. */ SynthesiserSound* addSound (const SynthesiserSound::Ptr& newSound); /** Removes and deletes one of the sounds. */ void removeSound (int index); //============================================================================== /** If set to true, then the synth will try to take over an existing voice if it runs out and needs to play another note. The value of this boolean is passed into findFreeVoice(), so the result will depend on the implementation of this method. */ void setNoteStealingEnabled (bool shouldStealNotes); /** Returns true if note-stealing is enabled. @see setNoteStealingEnabled */ bool isNoteStealingEnabled() const noexcept { return shouldStealNotes; } //============================================================================== /** Triggers a note-on event. The default method here will find all the sounds that want to be triggered by this note/channel. For each sound, it'll try to find a free voice, and use the voice to start playing the sound. Subclasses might want to override this if they need a more complex algorithm. This method will be called automatically according to the midi data passed into renderNextBlock(), but may be called explicitly too. The midiChannel parameter is the channel, between 1 and 16 inclusive. */ virtual void noteOn (int midiChannel, int midiNoteNumber, float velocity); /** Triggers a note-off event. This will turn off any voices that are playing a sound for the given note/channel. If allowTailOff is true, the voices will be allowed to fade out the notes gracefully (if they can do). If this is false, the notes will all be cut off immediately. This method will be called automatically according to the midi data passed into renderNextBlock(), but may be called explicitly too. The midiChannel parameter is the channel, between 1 and 16 inclusive. */ virtual void noteOff (int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff); /** Turns off all notes. This will turn off any voices that are playing a sound on the given midi channel. If midiChannel is 0 or less, then all voices will be turned off, regardless of which channel they're playing. Otherwise it represents a valid midi channel, from 1 to 16 inclusive. If allowTailOff is true, the voices will be allowed to fade out the notes gracefully (if they can do). If this is false, the notes will all be cut off immediately. This method will be called automatically according to the midi data passed into renderNextBlock(), but may be called explicitly too. */ virtual void allNotesOff (int midiChannel, bool allowTailOff); /** Sends a pitch-wheel message to any active voices. This will send a pitch-wheel message to any voices that are playing sounds on the given midi channel. This method will be called automatically according to the midi data passed into renderNextBlock(), but may be called explicitly too. @param midiChannel the midi channel, from 1 to 16 inclusive @param wheelValue the wheel position, from 0 to 0x3fff, as returned by MidiMessage::getPitchWheelValue() */ virtual void handlePitchWheel (int midiChannel, int wheelValue); /** Sends a midi controller message to any active voices. This will send a midi controller message to any voices that are playing sounds on the given midi channel. This method will be called automatically according to the midi data passed into renderNextBlock(), but may be called explicitly too. @param midiChannel the midi channel, from 1 to 16 inclusive @param controllerNumber the midi controller type, as returned by MidiMessage::getControllerNumber() @param controllerValue the midi controller value, between 0 and 127, as returned by MidiMessage::getControllerValue() */ virtual void handleController (int midiChannel, int controllerNumber, int controllerValue); /** Sends an aftertouch message. This will send an aftertouch message to any voices that are playing sounds on the given midi channel and note number. This method will be called automatically according to the midi data passed into renderNextBlock(), but may be called explicitly too. @param midiChannel the midi channel, from 1 to 16 inclusive @param midiNoteNumber the midi note number, 0 to 127 @param aftertouchValue the aftertouch value, between 0 and 127, as returned by MidiMessage::getAftertouchValue() */ virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); /** Sends a channel pressure message. This will send a channel pressure message to any voices that are playing sounds on the given midi channel. This method will be called automatically according to the midi data passed into renderNextBlock(), but may be called explicitly too. @param midiChannel the midi channel, from 1 to 16 inclusive @param channelPressureValue the pressure value, between 0 and 127, as returned by MidiMessage::getChannelPressureValue() */ virtual void handleChannelPressure (int midiChannel, int channelPressureValue); /** Handles a sustain pedal event. */ virtual void handleSustainPedal (int midiChannel, bool isDown); /** Handles a sostenuto pedal event. */ virtual void handleSostenutoPedal (int midiChannel, bool isDown); /** Can be overridden to handle soft pedal events. */ virtual void handleSoftPedal (int midiChannel, bool isDown); /** Can be overridden to handle an incoming program change message. The base class implementation of this has no effect, but you may want to make your own synth react to program changes. */ virtual void handleProgramChange (int midiChannel, int programNumber); //============================================================================== /** Tells the synthesiser what the sample rate is for the audio it's being used to render. This value is propagated to the voices so that they can use it to render the correct pitches. */ virtual void setCurrentPlaybackSampleRate (double sampleRate); /** Creates the next block of audio output. This will process the next numSamples of data from all the voices, and add that output to the audio block supplied, starting from the offset specified. Note that the data will be added to the current contents of the buffer, so you should clear it before calling this method if necessary. The midi events in the inputMidi buffer are parsed for note and controller events, and these are used to trigger the voices. Note that the startSample offset applies both to the audio output buffer and the midi input buffer, so any midi events with timestamps outside the specified region will be ignored. */ void renderNextBlock (AudioSampleBuffer& outputAudio, const MidiBuffer& inputMidi, int startSample, int numSamples); /** Returns the current target sample rate at which rendering is being done. Subclasses may need to know this so that they can pitch things correctly. */ double getSampleRate() const noexcept { return sampleRate; } /** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering. When rendering, the audio blocks that are passed into renderNextBlock() will be split up into smaller blocks that lie between all the incoming midi messages, and it is these smaller sub-blocks that are rendered with multiple calls to renderVoices(). Obviously in a pathological case where there are midi messages on every sample, then renderVoices() could be called once per sample and lead to poor performance, so this setting allows you to set a lower limit on the block size. The default setting is 32, which means that midi messages are accurate to about < 1ms accuracy, which is probably fine for most purposes, but you may want to increase or decrease this value for your synth. */ void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; protected: //============================================================================== /** This is used to control access to the rendering callback and the note trigger methods. */ CriticalSection lock; OwnedArray voices; ReferenceCountedArray sounds; /** The last pitch-wheel values for each midi channel. */ int lastPitchWheelValues [16]; /** Renders the voices for the given range. By default this just calls renderNextBlock() on each voice, but you may need to override it to handle custom cases. */ virtual void renderVoices (AudioSampleBuffer& outputAudio, int startSample, int numSamples); /** Searches through the voices to find one that's not currently playing, and which can play the given sound. Returns nullptr if all voices are busy and stealing isn't enabled. To implement a custom note-stealing algorithm, you can either override this method, or (preferably) override findVoiceToSteal(). */ virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay, int midiChannel, int midiNoteNumber, bool stealIfNoneAvailable) const; /** Chooses a voice that is most suitable for being re-used. The default method will attempt to find the oldest voice that isn't the bottom or top note being played. If that's not suitable for your synth, you can override this method and do something more cunning instead. */ virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay, int midiChannel, int midiNoteNumber) const; /** Starts a specified voice playing a particular sound. You'll probably never need to call this, it's used internally by noteOn(), but may be needed by subclasses for custom behaviours. */ void startVoice (SynthesiserVoice* voice, SynthesiserSound* sound, int midiChannel, int midiNoteNumber, float velocity); /** Can be overridden to do custom handling of incoming midi events. */ virtual void handleMidiEvent (const MidiMessage&); private: //============================================================================== double sampleRate; uint32 lastNoteOnCounter; int minimumSubBlockSize; bool shouldStealNotes; BigInteger sustainPedalsDown; void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff); #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // Note the new parameters for these methods. virtual int findFreeVoice (const bool) const { return 0; } virtual int noteOff (int, int, int) { return 0; } virtual int findFreeVoice (SynthesiserSound*, const bool) { return 0; } virtual int findVoiceToSteal (SynthesiserSound*) const { return 0; } #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) }; #endif // JUCE_SYNTHESISER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/000077500000000000000000000000001320201440200254155ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_cd/000077500000000000000000000000001320201440200271645ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h000066400000000000000000000146321320201440200327770ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOCDBURNER_H_INCLUDED #define JUCE_AUDIOCDBURNER_H_INCLUDED #if JUCE_USE_CDBURNER || DOXYGEN //============================================================================== /** */ class AudioCDBurner : public ChangeBroadcaster { public: //============================================================================== /** Returns a list of available optical drives. Use openDevice() to open one of the items from this list. */ static StringArray findAvailableDevices(); /** Tries to open one of the optical drives. The deviceIndex is an index into the array returned by findAvailableDevices(). */ static AudioCDBurner* openDevice (const int deviceIndex); /** Destructor. */ ~AudioCDBurner(); //============================================================================== enum DiskState { unknown, /**< An error condition, if the device isn't responding. */ trayOpen, /**< The drive is currently open. Note that a slot-loading drive may seem to be permanently open. */ noDisc, /**< The drive has no disk in it. */ writableDiskPresent, /**< The drive contains a writeable disk. */ readOnlyDiskPresent /**< The drive contains a read-only disk. */ }; /** Returns the current status of the device. To get informed when the drive's status changes, attach a ChangeListener to the AudioCDBurner. */ DiskState getDiskState() const; /** Returns true if there's a writable disk in the drive. */ bool isDiskPresent() const; /** Sends an eject signal to the drive. The eject will happen asynchronously, so you can use getDiskState() and waitUntilStateChange() to monitor its progress. */ bool openTray(); /** Blocks the current thread until the drive's state changes, or until the timeout expires. @returns the device's new state */ DiskState waitUntilStateChange (int timeOutMilliseconds); //============================================================================== /** Returns the set of possible write speeds that the device can handle. These are as a multiple of 'normal' speed, so e.g. '24x' returns 24, etc. Note that if there's no media present in the drive, this value may be unavailable! @see setWriteSpeed, getWriteSpeed */ Array getAvailableWriteSpeeds() const; //============================================================================== /** Tries to enable or disable buffer underrun safety on devices that support it. @returns true if it's now enabled. If the device doesn't support it, this will always return false. */ bool setBufferUnderrunProtection (bool shouldBeEnabled); //============================================================================== /** Returns the number of free blocks on the disk. There are 75 blocks per second, at 44100Hz. */ int getNumAvailableAudioBlocks() const; /** Adds a track to be written. The source passed-in here will be kept by this object, and it will be used and deleted at some point in the future, either during the burn() method or when this AudioCDBurner object is deleted. Your caller method shouldn't keep a reference to it or use it again after passing it in here. */ bool addAudioTrack (AudioSource* source, int numSamples); //============================================================================== /** Receives progress callbacks during a cd-burn operation. @see AudioCDBurner::burn() */ class BurnProgressListener { public: BurnProgressListener() noexcept {} virtual ~BurnProgressListener() {} /** Called at intervals to report on the progress of the AudioCDBurner. To cancel the burn, return true from this method. */ virtual bool audioCDBurnProgress (float proportionComplete) = 0; }; /** Runs the burn process. This method will block until the operation is complete. @param listener the object to receive callbacks about progress @param ejectDiscAfterwards whether to eject the disk after the burn completes @param performFakeBurnForTesting if true, no data will actually be written to the disk @param writeSpeed one of the write speeds from getAvailableWriteSpeeds(), or 0 or less to mean the fastest speed. */ String burn (BurnProgressListener* listener, bool ejectDiscAfterwards, bool performFakeBurnForTesting, int writeSpeed); /** If a burn operation is currently in progress, this tells it to stop as soon as possible. It's also possible to stop the burn process by returning true from BurnProgressListener::audioCDBurnProgress() */ void abortBurn(); private: //============================================================================== AudioCDBurner (const int deviceIndex); class Pimpl; friend struct ContainerDeletePolicy; ScopedPointer pimpl; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDBurner) }; #endif #endif // JUCE_AUDIOCDBURNER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp000066400000000000000000000033501320201440200332720ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_CDREADER int AudioCDReader::getNumTracks() const { return trackStartSamples.size() - 1; } int AudioCDReader::getPositionOfTrackStart (int trackNum) const { return trackStartSamples [trackNum]; } const Array& AudioCDReader::getTrackOffsets() const { return trackStartSamples; } int AudioCDReader::getCDDBId() { int checksum = 0; const int numTracks = getNumTracks(); for (int i = 0; i < numTracks; ++i) for (int offset = (trackStartSamples.getUnchecked(i) + 88200) / 44100; offset > 0; offset /= 10) checksum += offset % 10; const int length = (trackStartSamples.getLast() - trackStartSamples.getFirst()) / 44100; // CCLLLLTT: checksum, length, tracks return ((checksum & 0xff) << 24) | (length << 8) | numTracks; } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h000066400000000000000000000142121320201440200327360ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOCDREADER_H_INCLUDED #define JUCE_AUDIOCDREADER_H_INCLUDED #if JUCE_USE_CDREADER || DOXYGEN //============================================================================== /** A type of AudioFormatReader that reads from an audio CD. One of these can be used to read a CD as if it's one big audio stream. Use the getPositionOfTrackStart() method to find where the individual tracks are within the stream. @see AudioFormatReader */ class JUCE_API AudioCDReader : public AudioFormatReader { public: //============================================================================== /** Returns a list of names of Audio CDs currently available for reading. If there's a CD drive but no CD in it, this might return an empty list, or possibly a device that can be opened but which has no tracks, depending on the platform. @see createReaderForCD */ static StringArray getAvailableCDNames(); /** Tries to create an AudioFormatReader that can read from an Audio CD. @param index the index of one of the available CDs - use getAvailableCDNames() to find out how many there are. @returns a new AudioCDReader object, or nullptr if it couldn't be created. The caller will be responsible for deleting the object returned. */ static AudioCDReader* createReaderForCD (const int index); //============================================================================== /** Destructor. */ ~AudioCDReader(); /** Implementation of the AudioFormatReader method. */ bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override; /** Checks whether the CD has been removed from the drive. */ bool isCDStillPresent() const; /** Returns the total number of tracks (audio + data). */ int getNumTracks() const; /** Finds the sample offset of the start of a track. @param trackNum the track number, where trackNum = 0 is the first track and trackNum = getNumTracks() means the end of the CD. */ int getPositionOfTrackStart (int trackNum) const; /** Returns true if a given track is an audio track. @param trackNum the track number, where 0 is the first track. */ bool isTrackAudio (int trackNum) const; /** Returns an array of sample offsets for the start of each track, followed by the sample position of the end of the CD. */ const Array& getTrackOffsets() const; /** Refreshes the object's table of contents. If the disc has been ejected and a different one put in since this object was created, this will cause it to update its idea of how many tracks there are, etc. */ void refreshTrackLengths(); /** Enables scanning for indexes within tracks. @see getLastIndex */ void enableIndexScanning (bool enabled); /** Returns the index number found during the last read() call. Index scanning is turned off by default - turn it on with enableIndexScanning(). Then when the read() method is called, if it comes across an index within that block, the index number is stored and returned by this method. Some devices might not support indexes, of course. (If you don't know what CD indexes are, it's unlikely you'll ever need them). @see enableIndexScanning */ int getLastIndex() const; /** Scans a track to find the position of any indexes within it. @param trackNumber the track to look in, where 0 is the first track on the disc @returns an array of sample positions of any index points found (not including the index that marks the start of the track) */ Array findIndexesInTrack (const int trackNumber); /** Returns the CDDB id number for the CD. It's not a great way of identifying a disc, but it's traditional. */ int getCDDBId(); /** Tries to eject the disk. Ejecting the disk might not actually be possible, e.g. if some other process is using it. */ void ejectDisk(); //============================================================================== enum { framesPerSecond = 75, samplesPerFrame = 44100 / framesPerSecond }; private: //============================================================================== Array trackStartSamples; #if JUCE_MAC File volumeDir; Array tracks; int currentReaderTrack; ScopedPointer reader; AudioCDReader (const File& volume); #elif JUCE_WINDOWS bool audioTracks [100]; void* handle; MemoryBlock buffer; bool indexingEnabled; int lastIndex, firstFrameInBuffer, samplesInBuffer; AudioCDReader (void* handle); int getIndexAt (int samplePos); #elif JUCE_LINUX AudioCDReader(); #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDReader) }; #endif #endif // JUCE_AUDIOCDREADER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/000077500000000000000000000000001320201440200272055ustar00rootroot00000000000000juce_AudioDeviceManager.cpp000066400000000000000000001025461320201440200343240ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioDeviceManager::AudioDeviceSetup::AudioDeviceSetup() : sampleRate (0), bufferSize (0), useDefaultInputChannels (true), useDefaultOutputChannels (true) { } bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const { return outputDeviceName == other.outputDeviceName && inputDeviceName == other.inputDeviceName && sampleRate == other.sampleRate && bufferSize == other.bufferSize && inputChannels == other.inputChannels && useDefaultInputChannels == other.useDefaultInputChannels && outputChannels == other.outputChannels && useDefaultOutputChannels == other.useDefaultOutputChannels; } //============================================================================== class AudioDeviceManager::CallbackHandler : public AudioIODeviceCallback, public MidiInputCallback, public AudioIODeviceType::Listener { public: CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {} private: void audioDeviceIOCallback (const float** ins, int numIns, float** outs, int numOuts, int numSamples) override { owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples); } void audioDeviceAboutToStart (AudioIODevice* device) override { owner.audioDeviceAboutToStartInt (device); } void audioDeviceStopped() override { owner.audioDeviceStoppedInt(); } void audioDeviceError (const String& message) override { owner.audioDeviceErrorInt (message); } void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override { owner.handleIncomingMidiMessageInt (source, message); } void audioDeviceListChanged() override { owner.audioDeviceListChanged(); } AudioDeviceManager& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler) }; //============================================================================== AudioDeviceManager::AudioDeviceManager() : numInputChansNeeded (0), numOutputChansNeeded (2), listNeedsScanning (true), inputLevel (0), testSoundPosition (0), cpuUsageMs (0), timeToCpuScale (0) { callbackHandler = new CallbackHandler (*this); } AudioDeviceManager::~AudioDeviceManager() { currentAudioDevice = nullptr; defaultMidiOutput = nullptr; } //============================================================================== void AudioDeviceManager::createDeviceTypesIfNeeded() { if (availableDeviceTypes.size() == 0) { OwnedArray types; createAudioDeviceTypes (types); for (int i = 0; i < types.size(); ++i) addAudioDeviceType (types.getUnchecked(i)); types.clear (false); if (AudioIODeviceType* first = availableDeviceTypes.getFirst()) currentDeviceType = first->getTypeName(); } } const OwnedArray& AudioDeviceManager::getAvailableDeviceTypes() { scanDevicesIfNeeded(); return availableDeviceTypes; } void AudioDeviceManager::audioDeviceListChanged() { if (currentAudioDevice != nullptr) { currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); } sendChangeMessage(); } //============================================================================== static void addIfNotNull (OwnedArray& list, AudioIODeviceType* const device) { if (device != nullptr) list.add (device); } void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& list) { addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (false)); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (true)); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ALSA()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_OpenSLES()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android()); } void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType) { if (newDeviceType != nullptr) { jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size()); availableDeviceTypes.add (newDeviceType); lastDeviceTypeConfigs.add (new AudioDeviceSetup()); newDeviceType->addListener (callbackHandler); } } static bool deviceListContains (AudioIODeviceType* type, bool isInput, const String& name) { StringArray devices (type->getDeviceNames (isInput)); for (int i = devices.size(); --i >= 0;) if (devices[i].trim().equalsIgnoreCase (name.trim())) return true; return false; } //============================================================================== String AudioDeviceManager::initialise (const int numInputChannelsNeeded, const int numOutputChannelsNeeded, const XmlElement* const xml, const bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName, const AudioDeviceSetup* preferredSetupOptions) { scanDevicesIfNeeded(); numInputChansNeeded = numInputChannelsNeeded; numOutputChansNeeded = numOutputChannelsNeeded; if (xml != nullptr && xml->hasTagName ("DEVICESETUP")) return initialiseFromXML (*xml, selectDefaultDeviceOnFailure, preferredDefaultDeviceName, preferredSetupOptions); return initialiseDefault (preferredDefaultDeviceName, preferredSetupOptions); } String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup* preferredSetupOptions) { AudioDeviceSetup setup; if (preferredSetupOptions != nullptr) { setup = *preferredSetupOptions; } else if (preferredDefaultDeviceName.isNotEmpty()) { for (int j = availableDeviceTypes.size(); --j >= 0;) { AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); const StringArray outs (type->getDeviceNames (false)); for (int i = 0; i < outs.size(); ++i) { if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) { setup.outputDeviceName = outs[i]; break; } } const StringArray ins (type->getDeviceNames (true)); for (int i = 0; i < ins.size(); ++i) { if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) { setup.inputDeviceName = ins[i]; break; } } } } insertDefaultDeviceNames (setup); return setAudioDeviceSetup (setup, false); } String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, const bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName, const AudioDeviceSetup* preferredSetupOptions) { lastExplicitSettings = new XmlElement (xml); String error; AudioDeviceSetup setup; if (preferredSetupOptions != nullptr) setup = *preferredSetupOptions; if (xml.getStringAttribute ("audioDeviceName").isNotEmpty()) { setup.inputDeviceName = setup.outputDeviceName = xml.getStringAttribute ("audioDeviceName"); } else { setup.inputDeviceName = xml.getStringAttribute ("audioInputDeviceName"); setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName"); } currentDeviceType = xml.getStringAttribute ("deviceType"); if (findType (currentDeviceType) == nullptr) { if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName)) currentDeviceType = type->getTypeName(); else if (availableDeviceTypes.size() > 0) currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); } setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize"); setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate"); setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2); setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2); setup.useDefaultInputChannels = ! xml.hasAttribute ("audioDeviceInChans"); setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans"); error = setAudioDeviceSetup (setup, true); midiInsFromXml.clear(); forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT") midiInsFromXml.add (c->getStringAttribute ("name")); const StringArray allMidiIns (MidiInput::getDevices()); for (int i = allMidiIns.size(); --i >= 0;) setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); if (error.isNotEmpty() && selectDefaultDeviceOnFailure) error = initialise (numInputChansNeeded, numOutputChansNeeded, nullptr, false, preferredDefaultDeviceName); setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput")); return error; } String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded, int numOutputChannelsNeeded) { lastExplicitSettings = nullptr; return initialise (numInputChannelsNeeded, numOutputChannelsNeeded, nullptr, false, String(), nullptr); } void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const { if (AudioIODeviceType* type = getCurrentDeviceTypeObject()) { if (setup.outputDeviceName.isEmpty()) setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)]; if (setup.inputDeviceName.isEmpty()) setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)]; } } XmlElement* AudioDeviceManager::createStateXml() const { return lastExplicitSettings.createCopy(); } //============================================================================== void AudioDeviceManager::scanDevicesIfNeeded() { if (listNeedsScanning) { listNeedsScanning = false; createDeviceTypesIfNeeded(); for (int i = availableDeviceTypes.size(); --i >= 0;) availableDeviceTypes.getUnchecked(i)->scanForDevices(); } } AudioIODeviceType* AudioDeviceManager::findType (const String& typeName) { scanDevicesIfNeeded(); for (int i = availableDeviceTypes.size(); --i >= 0;) if (availableDeviceTypes.getUnchecked(i)->getTypeName() == typeName) return availableDeviceTypes.getUnchecked(i); return nullptr; } AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName) { scanDevicesIfNeeded(); for (int i = availableDeviceTypes.size(); --i >= 0;) { AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(i); if ((inputName.isNotEmpty() && deviceListContains (type, true, inputName)) || (outputName.isNotEmpty() && deviceListContains (type, false, outputName))) { return type; } } return nullptr; } void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup) { setup = currentSetup; } void AudioDeviceManager::deleteCurrentDevice() { currentAudioDevice = nullptr; currentSetup.inputDeviceName.clear(); currentSetup.outputDeviceName.clear(); } void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, const bool treatAsChosenDevice) { for (int i = 0; i < availableDeviceTypes.size(); ++i) { if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type && currentDeviceType != type) { if (currentAudioDevice != nullptr) { closeAudioDevice(); Thread::sleep (1500); // allow a moment for OS devices to sort themselves out, to help // avoid things like DirectSound/ASIO clashes } currentDeviceType = type; AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i)); insertDefaultDeviceNames (s); setAudioDeviceSetup (s, treatAsChosenDevice); sendChangeMessage(); break; } } } AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const { for (int i = 0; i < availableDeviceTypes.size(); ++i) if (availableDeviceTypes.getUnchecked(i)->getTypeName() == currentDeviceType) return availableDeviceTypes.getUnchecked(i); return availableDeviceTypes[0]; } String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup, const bool treatAsChosenDevice) { jassert (&newSetup != ¤tSetup); // this will have no effect if (newSetup == currentSetup && currentAudioDevice != nullptr) return String(); if (! (newSetup == currentSetup)) sendChangeMessage(); stopDevice(); const String newInputDeviceName (numInputChansNeeded == 0 ? String() : newSetup.inputDeviceName); const String newOutputDeviceName (numOutputChansNeeded == 0 ? String() : newSetup.outputDeviceName); String error; AudioIODeviceType* type = getCurrentDeviceTypeObject(); if (type == nullptr || (newInputDeviceName.isEmpty() && newOutputDeviceName.isEmpty())) { deleteCurrentDevice(); if (treatAsChosenDevice) updateXml(); return String(); } if (currentSetup.inputDeviceName != newInputDeviceName || currentSetup.outputDeviceName != newOutputDeviceName || currentAudioDevice == nullptr) { deleteCurrentDevice(); scanDevicesIfNeeded(); if (newOutputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newOutputDeviceName)) return "No such device: " + newOutputDeviceName; if (newInputDeviceName.isNotEmpty() && ! deviceListContains (type, true, newInputDeviceName)) return "No such device: " + newInputDeviceName; currentAudioDevice = type->createDevice (newOutputDeviceName, newInputDeviceName); if (currentAudioDevice == nullptr) error = "Can't open the audio device!\n\n" "This may be because another application is currently using the same device - " "if so, you should close any other applications and try again!"; else error = currentAudioDevice->getLastError(); if (error.isNotEmpty()) { deleteCurrentDevice(); return error; } if (newSetup.useDefaultInputChannels) { inputChannels.clear(); inputChannels.setRange (0, numInputChansNeeded, true); } if (newSetup.useDefaultOutputChannels) { outputChannels.clear(); outputChannels.setRange (0, numOutputChansNeeded, true); } if (newInputDeviceName.isEmpty()) inputChannels.clear(); if (newOutputDeviceName.isEmpty()) outputChannels.clear(); } if (! newSetup.useDefaultInputChannels) inputChannels = newSetup.inputChannels; if (! newSetup.useDefaultOutputChannels) outputChannels = newSetup.outputChannels; currentSetup = newSetup; currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate); currentSetup.bufferSize = chooseBestBufferSize (newSetup.bufferSize); error = currentAudioDevice->open (inputChannels, outputChannels, currentSetup.sampleRate, currentSetup.bufferSize); if (error.isEmpty()) { currentDeviceType = currentAudioDevice->getTypeName(); currentAudioDevice->start (callbackHandler); currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); for (int i = 0; i < availableDeviceTypes.size(); ++i) if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType) *(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup; if (treatAsChosenDevice) updateXml(); } else { deleteCurrentDevice(); } return error; } double AudioDeviceManager::chooseBestSampleRate (double rate) const { jassert (currentAudioDevice != nullptr); const Array rates (currentAudioDevice->getAvailableSampleRates()); if (rate > 0 && rates.contains (rate)) return rate; rate = currentAudioDevice->getCurrentSampleRate(); if (rate > 0 && rates.contains (rate)) return rate; double lowestAbove44 = 0.0; for (int i = rates.size(); --i >= 0;) { const double sr = rates[i]; if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44)) lowestAbove44 = sr; } if (lowestAbove44 > 0.0) return lowestAbove44; return rates[0]; } int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const { jassert (currentAudioDevice != nullptr); if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize)) return bufferSize; return currentAudioDevice->getDefaultBufferSize(); } void AudioDeviceManager::stopDevice() { if (currentAudioDevice != nullptr) currentAudioDevice->stop(); testSound = nullptr; } void AudioDeviceManager::closeAudioDevice() { stopDevice(); currentAudioDevice = nullptr; } void AudioDeviceManager::restartLastAudioDevice() { if (currentAudioDevice == nullptr) { if (currentSetup.inputDeviceName.isEmpty() && currentSetup.outputDeviceName.isEmpty()) { // This method will only reload the last device that was running // before closeAudioDevice() was called - you need to actually open // one first, with setAudioDevice(). jassertfalse; return; } AudioDeviceSetup s (currentSetup); setAudioDeviceSetup (s, false); } } void AudioDeviceManager::updateXml() { lastExplicitSettings = new XmlElement ("DEVICESETUP"); lastExplicitSettings->setAttribute ("deviceType", currentDeviceType); lastExplicitSettings->setAttribute ("audioOutputDeviceName", currentSetup.outputDeviceName); lastExplicitSettings->setAttribute ("audioInputDeviceName", currentSetup.inputDeviceName); if (currentAudioDevice != nullptr) { lastExplicitSettings->setAttribute ("audioDeviceRate", currentAudioDevice->getCurrentSampleRate()); if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples()) lastExplicitSettings->setAttribute ("audioDeviceBufferSize", currentAudioDevice->getCurrentBufferSizeSamples()); if (! currentSetup.useDefaultInputChannels) lastExplicitSettings->setAttribute ("audioDeviceInChans", currentSetup.inputChannels.toString (2)); if (! currentSetup.useDefaultOutputChannels) lastExplicitSettings->setAttribute ("audioDeviceOutChans", currentSetup.outputChannels.toString (2)); } for (int i = 0; i < enabledMidiInputs.size(); ++i) lastExplicitSettings->createNewChildElement ("MIDIINPUT") ->setAttribute ("name", enabledMidiInputs[i]->getName()); if (midiInsFromXml.size() > 0) { // Add any midi devices that have been enabled before, but which aren't currently // open because the device has been disconnected. const StringArray availableMidiDevices (MidiInput::getDevices()); for (int i = 0; i < midiInsFromXml.size(); ++i) if (! availableMidiDevices.contains (midiInsFromXml[i], true)) lastExplicitSettings->createNewChildElement ("MIDIINPUT") ->setAttribute ("name", midiInsFromXml[i]); } if (defaultMidiOutputName.isNotEmpty()) lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputName); } //============================================================================== void AudioDeviceManager::addAudioCallback (AudioIODeviceCallback* newCallback) { { const ScopedLock sl (audioCallbackLock); if (callbacks.contains (newCallback)) return; } if (currentAudioDevice != nullptr && newCallback != nullptr) newCallback->audioDeviceAboutToStart (currentAudioDevice); const ScopedLock sl (audioCallbackLock); callbacks.add (newCallback); } void AudioDeviceManager::removeAudioCallback (AudioIODeviceCallback* callbackToRemove) { if (callbackToRemove != nullptr) { bool needsDeinitialising = currentAudioDevice != nullptr; { const ScopedLock sl (audioCallbackLock); needsDeinitialising = needsDeinitialising && callbacks.contains (callbackToRemove); callbacks.removeFirstMatchingValue (callbackToRemove); } if (needsDeinitialising) callbackToRemove->audioDeviceStopped(); } } void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData, int numInputChannels, float** outputChannelData, int numOutputChannels, int numSamples) { const ScopedLock sl (audioCallbackLock); if (inputLevelMeasurementEnabledCount.get() > 0 && numInputChannels > 0) { for (int j = 0; j < numSamples; ++j) { float s = 0; for (int i = 0; i < numInputChannels; ++i) s += std::abs (inputChannelData[i][j]); s /= numInputChannels; const double decayFactor = 0.99992; if (s > inputLevel) inputLevel = s; else if (inputLevel > 0.001f) inputLevel *= decayFactor; else inputLevel = 0; } } else { inputLevel = 0; } if (callbacks.size() > 0) { const double callbackStartTime = Time::getMillisecondCounterHiRes(); tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true); callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples); float** const tempChans = tempBuffer.getArrayOfWritePointers(); for (int i = callbacks.size(); --i > 0;) { callbacks.getUnchecked(i)->audioDeviceIOCallback (inputChannelData, numInputChannels, tempChans, numOutputChannels, numSamples); for (int chan = 0; chan < numOutputChannels; ++chan) { if (const float* const src = tempChans [chan]) if (float* const dst = outputChannelData [chan]) for (int j = 0; j < numSamples; ++j) dst[j] += src[j]; } } const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime; const double filterAmount = 0.2; cpuUsageMs += filterAmount * (msTaken - cpuUsageMs); } else { for (int i = 0; i < numOutputChannels; ++i) zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples); } if (testSound != nullptr) { const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition); const float* const src = testSound->getReadPointer (0, testSoundPosition); for (int i = 0; i < numOutputChannels; ++i) for (int j = 0; j < numSamps; ++j) outputChannelData [i][j] += src[j]; testSoundPosition += numSamps; if (testSoundPosition >= testSound->getNumSamples()) testSound = nullptr; } } void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device) { cpuUsageMs = 0; const double sampleRate = device->getCurrentSampleRate(); const int blockSize = device->getCurrentBufferSizeSamples(); if (sampleRate > 0.0 && blockSize > 0) { const double msPerBlock = 1000.0 * blockSize / sampleRate; timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0; } { const ScopedLock sl (audioCallbackLock); for (int i = callbacks.size(); --i >= 0;) callbacks.getUnchecked(i)->audioDeviceAboutToStart (device); } sendChangeMessage(); } void AudioDeviceManager::audioDeviceStoppedInt() { cpuUsageMs = 0; timeToCpuScale = 0; sendChangeMessage(); const ScopedLock sl (audioCallbackLock); for (int i = callbacks.size(); --i >= 0;) callbacks.getUnchecked(i)->audioDeviceStopped(); } void AudioDeviceManager::audioDeviceErrorInt (const String& message) { const ScopedLock sl (audioCallbackLock); for (int i = callbacks.size(); --i >= 0;) callbacks.getUnchecked(i)->audioDeviceError (message); } double AudioDeviceManager::getCpuUsage() const { return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); } //============================================================================== void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled) { if (enabled != isMidiInputEnabled (name)) { if (enabled) { const int index = MidiInput::getDevices().indexOf (name); if (index >= 0) { if (MidiInput* const midiIn = MidiInput::openDevice (index, callbackHandler)) { enabledMidiInputs.add (midiIn); midiIn->start(); } } } else { for (int i = enabledMidiInputs.size(); --i >= 0;) if (enabledMidiInputs[i]->getName() == name) enabledMidiInputs.remove (i); } updateXml(); sendChangeMessage(); } } bool AudioDeviceManager::isMidiInputEnabled (const String& name) const { for (int i = enabledMidiInputs.size(); --i >= 0;) if (enabledMidiInputs[i]->getName() == name) return true; return false; } void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd) { removeMidiInputCallback (name, callbackToAdd); if (name.isEmpty() || isMidiInputEnabled (name)) { const ScopedLock sl (midiCallbackLock); MidiCallbackInfo mc; mc.deviceName = name; mc.callback = callbackToAdd; midiCallbacks.add (mc); } } void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove) { for (int i = midiCallbacks.size(); --i >= 0;) { const MidiCallbackInfo& mc = midiCallbacks.getReference(i); if (mc.callback == callbackToRemove && mc.deviceName == name) { const ScopedLock sl (midiCallbackLock); midiCallbacks.remove (i); } } } void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message) { if (! message.isActiveSense()) { const ScopedLock sl (midiCallbackLock); for (int i = 0; i < midiCallbacks.size(); ++i) { const MidiCallbackInfo& mc = midiCallbacks.getReference(i); if (mc.deviceName.isEmpty() || mc.deviceName == source->getName()) mc.callback->handleIncomingMidiMessage (source, message); } } } //============================================================================== void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) { if (defaultMidiOutputName != deviceName) { Array oldCallbacks; { const ScopedLock sl (audioCallbackLock); oldCallbacks.swapWith (callbacks); } if (currentAudioDevice != nullptr) for (int i = oldCallbacks.size(); --i >= 0;) oldCallbacks.getUnchecked(i)->audioDeviceStopped(); defaultMidiOutput = nullptr; defaultMidiOutputName = deviceName; if (deviceName.isNotEmpty()) defaultMidiOutput = MidiOutput::openDevice (MidiOutput::getDevices().indexOf (deviceName)); if (currentAudioDevice != nullptr) for (int i = oldCallbacks.size(); --i >= 0;) oldCallbacks.getUnchecked(i)->audioDeviceAboutToStart (currentAudioDevice); { const ScopedLock sl (audioCallbackLock); oldCallbacks.swapWith (callbacks); } updateXml(); sendChangeMessage(); } } //============================================================================== void AudioDeviceManager::playTestSound() { { // cunningly nested to swap, unlock and delete in that order. ScopedPointer oldSound; { const ScopedLock sl (audioCallbackLock); oldSound = testSound; } } testSoundPosition = 0; if (currentAudioDevice != nullptr) { const double sampleRate = currentAudioDevice->getCurrentSampleRate(); const int soundLength = (int) sampleRate; const double frequency = 440.0; const float amplitude = 0.5f; const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); for (int i = 0; i < soundLength; ++i) newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample)); newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); const ScopedLock sl (audioCallbackLock); testSound = newSound; } } void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement) { if (enableMeasurement) ++inputLevelMeasurementEnabledCount; else --inputLevelMeasurementEnabledCount; inputLevel = 0; } double AudioDeviceManager::getCurrentInputLevel() const { jassert (inputLevelMeasurementEnabledCount.get() > 0); // you need to call enableInputLevelMeasurement() before using this! return inputLevel; } juce_AudioDeviceManager.h000066400000000000000000000561641320201440200337750ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIODEVICEMANAGER_H_INCLUDED #define JUCE_AUDIODEVICEMANAGER_H_INCLUDED //============================================================================== /** Manages the state of some audio and midi i/o devices. This class keeps tracks of a currently-selected audio device, through with which it continuously streams data from an audio callback, as well as one or more midi inputs. The idea is that your application will create one global instance of this object, and let it take care of creating and deleting specific types of audio devices internally. So when the device is changed, your callbacks will just keep running without having to worry about this. The manager can save and reload all of its device settings as XML, which makes it very easy for you to save and reload the audio setup of your application. And to make it easy to let the user change its settings, there's a component to do just that - the AudioDeviceSelectorComponent class, which contains a set of device selection/sample-rate/latency controls. To use an AudioDeviceManager, create one, and use initialise() to set it up. Then call addAudioCallback() to register your audio callback with it, and use that to process your audio data. The manager also acts as a handy hub for incoming midi messages, allowing a listener to register for messages from either a specific midi device, or from whatever the current default midi input device is. The listener then doesn't have to worry about re-registering with different midi devices if they are changed or deleted. And yet another neat trick is that amount of CPU time being used is measured and available with the getCpuUsage() method. The AudioDeviceManager is a ChangeBroadcaster, and will send a change message to listeners whenever one of its settings is changed. @see AudioDeviceSelectorComponent, AudioIODevice, AudioIODeviceType */ class JUCE_API AudioDeviceManager : public ChangeBroadcaster { public: //============================================================================== /** Creates a default AudioDeviceManager. Initially no audio device will be selected. You should call the initialise() method and register an audio callback with setAudioCallback() before it'll be able to actually make any noise. */ AudioDeviceManager(); /** Destructor. */ ~AudioDeviceManager(); //============================================================================== /** This structure holds a set of properties describing the current audio setup. An AudioDeviceManager uses this class to save/load its current settings, and to specify your preferred options when opening a device. @see AudioDeviceManager::setAudioDeviceSetup(), AudioDeviceManager::initialise() */ struct JUCE_API AudioDeviceSetup { /** Creates an AudioDeviceSetup object. The default constructor sets all the member variables to indicate default values. You can then fill-in any values you want to before passing the object to AudioDeviceManager::initialise(). */ AudioDeviceSetup(); bool operator== (const AudioDeviceSetup& other) const; /** The name of the audio device used for output. The name has to be one of the ones listed by the AudioDeviceManager's currently selected device type. This may be the same as the input device. An empty string indicates the default device. */ String outputDeviceName; /** The name of the audio device used for input. This may be the same as the output device. An empty string indicates the default device. */ String inputDeviceName; /** The current sample rate. This rate is used for both the input and output devices. A value of 0 indicates that you don't care what rate is used, and the device will choose a sensible rate for you. */ double sampleRate; /** The buffer size, in samples. This buffer size is used for both the input and output devices. A value of 0 indicates the default buffer size. */ int bufferSize; /** The set of active input channels. The bits that are set in this array indicate the channels of the input device that are active. If useDefaultInputChannels is true, this value is ignored. */ BigInteger inputChannels; /** If this is true, it indicates that the inputChannels array should be ignored, and instead, the device's default channels should be used. */ bool useDefaultInputChannels; /** The set of active output channels. The bits that are set in this array indicate the channels of the input device that are active. If useDefaultOutputChannels is true, this value is ignored. */ BigInteger outputChannels; /** If this is true, it indicates that the outputChannels array should be ignored, and instead, the device's default channels should be used. */ bool useDefaultOutputChannels; }; //============================================================================== /** Opens a set of audio devices ready for use. This will attempt to open either a default audio device, or one that was previously saved as XML. @param numInputChannelsNeeded the maximum number of input channels your app would like to use (the actual number of channels opened may be less than the number requested) @param numOutputChannelsNeeded the maximum number of output channels your app would like to use (the actual number of channels opened may be less than the number requested) @param savedState either a previously-saved state that was produced by createStateXml(), or nullptr if you want the manager to choose the best device to open. @param selectDefaultDeviceOnFailure if true, then if the device specified in the XML fails to open, then a default device will be used instead. If false, then on failure, no device is opened. @param preferredDefaultDeviceName if this is not empty, and there's a device with this name, then that will be used as the default device (assuming that there wasn't one specified in the XML). The string can actually be a simple wildcard, containing "*" and "?" characters @param preferredSetupOptions if this is non-null, the structure will be used as the set of preferred settings when opening the device. If you use this parameter, the preferredDefaultDeviceName field will be ignored @returns an error message if anything went wrong, or an empty string if it worked ok. */ String initialise (int numInputChannelsNeeded, int numOutputChannelsNeeded, const XmlElement* savedState, bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName = String(), const AudioDeviceSetup* preferredSetupOptions = nullptr); /** Resets everything to a default device setup, clearing any stored settings. */ String initialiseWithDefaultDevices (int numInputChannelsNeeded, int numOutputChannelsNeeded); /** Returns some XML representing the current state of the manager. This stores the current device, its samplerate, block size, etc, and can be restored later with initialise(). Note that this can return a null pointer if no settings have been explicitly changed (i.e. if the device manager has just been left in its default state). */ XmlElement* createStateXml() const; //============================================================================== /** Returns the current device properties that are in use. @see setAudioDeviceSetup */ void getAudioDeviceSetup (AudioDeviceSetup& result); /** Changes the current device or its settings. If you want to change a device property, like the current sample rate or block size, you can call getAudioDeviceSetup() to retrieve the current settings, then tweak the appropriate fields in the AudioDeviceSetup structure, and pass it back into this method to apply the new settings. @param newSetup the settings that you'd like to use @param treatAsChosenDevice if this is true and if the device opens correctly, these new settings will be taken as having been explicitly chosen by the user, and the next time createStateXml() is called, these settings will be returned. If it's false, then the device is treated as a temporary or default device, and a call to createStateXml() will return either the last settings that were made with treatAsChosenDevice as true, or the last XML settings that were passed into initialise(). @returns an error message if anything went wrong, or an empty string if it worked ok. @see getAudioDeviceSetup */ String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, bool treatAsChosenDevice); /** Returns the currently-active audio device. */ AudioIODevice* getCurrentAudioDevice() const noexcept { return currentAudioDevice; } /** Returns the type of audio device currently in use. @see setCurrentAudioDeviceType */ String getCurrentAudioDeviceType() const { return currentDeviceType; } /** Returns the currently active audio device type object. Don't keep a copy of this pointer - it's owned by the device manager and could change at any time. */ AudioIODeviceType* getCurrentDeviceTypeObject() const; /** Changes the class of audio device being used. This switches between, e.g. ASIO and DirectSound. On the Mac you probably won't ever call this because there's only one type: CoreAudio. For a list of types, see getAvailableDeviceTypes(). */ void setCurrentAudioDeviceType (const String& type, bool treatAsChosenDevice); /** Closes the currently-open device. You can call restartLastAudioDevice() later to reopen it in the same state that it was just in. */ void closeAudioDevice(); /** Tries to reload the last audio device that was running. Note that this only reloads the last device that was running before closeAudioDevice() was called - it doesn't reload any kind of saved-state, and can only be called after a device has been opened with SetAudioDevice(). If a device is already open, this call will do nothing. */ void restartLastAudioDevice(); //============================================================================== /** Registers an audio callback to be used. The manager will redirect callbacks from whatever audio device is currently in use to all registered callback objects. If more than one callback is active, they will all be given the same input data, and their outputs will be summed. If necessary, this method will invoke audioDeviceAboutToStart() on the callback object before returning. To remove a callback, use removeAudioCallback(). */ void addAudioCallback (AudioIODeviceCallback* newCallback); /** Deregisters a previously added callback. If necessary, this method will invoke audioDeviceStopped() on the callback object before returning. @see addAudioCallback */ void removeAudioCallback (AudioIODeviceCallback* callback); //============================================================================== /** Returns the average proportion of available CPU being spent inside the audio callbacks. @returns A value between 0 and 1.0 to indicate the approximate proportion of CPU time spent in the callbacks. */ double getCpuUsage() const; //============================================================================== /** Enables or disables a midi input device. The list of devices can be obtained with the MidiInput::getDevices() method. Any incoming messages from enabled input devices will be forwarded on to all the listeners that have been registered with the addMidiInputCallback() method. They can either register for messages from a particular device, or from just the "default" midi input. Routing the midi input via an AudioDeviceManager means that when a listener registers for the default midi input, this default device can be changed by the manager without the listeners having to know about it or re-register. It also means that a listener can stay registered for a midi input that is disabled or not present, so that when the input is re-enabled, the listener will start receiving messages again. @see addMidiInputCallback, isMidiInputEnabled */ void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled); /** Returns true if a given midi input device is being used. @see setMidiInputEnabled */ bool isMidiInputEnabled (const String& midiInputDeviceName) const; /** Registers a listener for callbacks when midi events arrive from a midi input. The device name can be empty to indicate that it wants to receive all incoming events from all the enabled MIDI inputs. Or it can be the name of one of the MIDI input devices if it just wants the events from that device. (see MidiInput::getDevices() for the list of device names). Only devices which are enabled (see the setMidiInputEnabled() method) will have their events forwarded on to listeners. */ void addMidiInputCallback (const String& midiInputDeviceName, MidiInputCallback* callback); /** Removes a listener that was previously registered with addMidiInputCallback(). */ void removeMidiInputCallback (const String& midiInputDeviceName, MidiInputCallback* callback); //============================================================================== /** Sets a midi output device to use as the default. The list of devices can be obtained with the MidiOutput::getDevices() method. The specified device will be opened automatically and can be retrieved with the getDefaultMidiOutput() method. Pass in an empty string to deselect all devices. For the default device, you can use MidiOutput::getDevices() [MidiOutput::getDefaultDeviceIndex()]. @see getDefaultMidiOutput, getDefaultMidiOutputName */ void setDefaultMidiOutput (const String& deviceName); /** Returns the name of the default midi output. @see setDefaultMidiOutput, getDefaultMidiOutput */ const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputName; } /** Returns the current default midi output device. If no device has been selected, or the device can't be opened, this will return nullptr. @see getDefaultMidiOutputName */ MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; } /** Returns a list of the types of device supported. */ const OwnedArray& getAvailableDeviceTypes(); //============================================================================== /** Creates a list of available types. This will add a set of new AudioIODeviceType objects to the specified list, to represent each available types of device. You can override this if your app needs to do something specific, like avoid using DirectSound devices, etc. */ virtual void createAudioDeviceTypes (OwnedArray& types); /** Adds a new device type to the list of types. The manager will take ownership of the object that is passed-in. */ void addAudioDeviceType (AudioIODeviceType* newDeviceType); //============================================================================== /** Plays a beep through the current audio device. This is here to allow the audio setup UI panels to easily include a "test" button so that the user can check where the audio is coming from. */ void playTestSound(); /** Turns on level-measuring. When enabled, the device manager will measure the peak input level across all channels, and you can get this level by calling getCurrentInputLevel(). This is mainly intended for audio setup UI panels to use to create a mic level display, so that the user can check that they've selected the right device. A simple filter is used to make the level decay smoothly, but this is only intended for giving rough feedback, and not for any kind of accurate measurement. */ void enableInputLevelMeasurement (bool enableMeasurement); /** Returns the current input level. To use this, you must first enable it by calling enableInputLevelMeasurement(). See enableInputLevelMeasurement() for more info. */ double getCurrentInputLevel() const; /** Returns the a lock that can be used to synchronise access to the audio callback. Obviously while this is locked, you're blocking the audio thread from running, so it must only be used for very brief periods when absolutely necessary. */ CriticalSection& getAudioCallbackLock() noexcept { return audioCallbackLock; } /** Returns the a lock that can be used to synchronise access to the midi callback. Obviously while this is locked, you're blocking the midi system from running, so it must only be used for very brief periods when absolutely necessary. */ CriticalSection& getMidiCallbackLock() noexcept { return midiCallbackLock; } private: //============================================================================== OwnedArray availableDeviceTypes; OwnedArray lastDeviceTypeConfigs; AudioDeviceSetup currentSetup; ScopedPointer currentAudioDevice; Array callbacks; int numInputChansNeeded, numOutputChansNeeded; String currentDeviceType; BigInteger inputChannels, outputChannels; ScopedPointer lastExplicitSettings; mutable bool listNeedsScanning; Atomic inputLevelMeasurementEnabledCount; double inputLevel; ScopedPointer testSound; int testSoundPosition; AudioSampleBuffer tempBuffer; struct MidiCallbackInfo { String deviceName; MidiInputCallback* callback; }; StringArray midiInsFromXml; OwnedArray enabledMidiInputs; Array midiCallbacks; String defaultMidiOutputName; ScopedPointer defaultMidiOutput; CriticalSection audioCallbackLock, midiCallbackLock; double cpuUsageMs, timeToCpuScale; //============================================================================== class CallbackHandler; friend class CallbackHandler; friend struct ContainerDeletePolicy; ScopedPointer callbackHandler; void audioDeviceIOCallbackInt (const float** inputChannelData, int totalNumInputChannels, float** outputChannelData, int totalNumOutputChannels, int numSamples); void audioDeviceAboutToStartInt (AudioIODevice*); void audioDeviceStoppedInt(); void audioDeviceErrorInt (const String&); void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); void audioDeviceListChanged(); String restartDevice (int blockSizeToUse, double sampleRateToUse, const BigInteger& ins, const BigInteger& outs); void stopDevice(); void updateXml(); void createDeviceTypesIfNeeded(); void scanDevicesIfNeeded(); void deleteCurrentDevice(); double chooseBestSampleRate (double preferred) const; int chooseBestBufferSize (int preferred) const; void insertDefaultDeviceNames (AudioDeviceSetup&) const; String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*); String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName, const AudioDeviceSetup*); AudioIODeviceType* findType (const String& inputName, const String& outputName); AudioIODeviceType* findType (const String& typeName); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager) }; #endif // JUCE_AUDIODEVICEMANAGER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp000066400000000000000000000030311320201440200333250ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName) : name (deviceName), typeName (deviceTypeName) { } AudioIODevice::~AudioIODevice() {} void AudioIODeviceCallback::audioDeviceError (const String&) {} bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; } bool AudioIODevice::hasControlPanel() const { return false; } bool AudioIODevice::showControlPanel() { jassertfalse; // this should only be called for devices which return true from // their hasControlPanel() method. return false; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h000066400000000000000000000321701320201440200330000ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOIODEVICE_H_INCLUDED #define JUCE_AUDIOIODEVICE_H_INCLUDED class AudioIODevice; //============================================================================== /** One of these is passed to an AudioIODevice object to stream the audio data in and out. The AudioIODevice will repeatedly call this class's audioDeviceIOCallback() method on its own high-priority audio thread, when it needs to send or receive the next block of data. @see AudioIODevice, AudioDeviceManager */ class JUCE_API AudioIODeviceCallback { public: /** Destructor. */ virtual ~AudioIODeviceCallback() {} /** Processes a block of incoming and outgoing audio data. The subclass's implementation should use the incoming audio for whatever purposes it needs to, and must fill all the output channels with the next block of output data before returning. The channel data is arranged with the same array indices as the channel name array returned by AudioIODevice::getOutputChannelNames(), but those channels that aren't specified in AudioIODevice::open() will have a null pointer for their associated channel, so remember to check for this. @param inputChannelData a set of arrays containing the audio data for each incoming channel - this data is valid until the function returns. There will be one channel of data for each input channel that was enabled when the audio device was opened (see AudioIODevice::open()) @param numInputChannels the number of pointers to channel data in the inputChannelData array. @param outputChannelData a set of arrays which need to be filled with the data that should be sent to each outgoing channel of the device. There will be one channel of data for each output channel that was enabled when the audio device was opened (see AudioIODevice::open()) The initial contents of the array is undefined, so the callback function must fill all the channels with zeros if its output is silence. Failing to do this could cause quite an unpleasant noise! @param numOutputChannels the number of pointers to channel data in the outputChannelData array. @param numSamples the number of samples in each channel of the input and output arrays. The number of samples will depend on the audio device's buffer size and will usually remain constant, although this isn't guaranteed, so make sure your code can cope with reasonable changes in the buffer size from one callback to the next. */ virtual void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels, float** outputChannelData, int numOutputChannels, int numSamples) = 0; /** Called to indicate that the device is about to start calling back. This will be called just before the audio callbacks begin, either when this callback has just been added to an audio device, or after the device has been restarted because of a sample-rate or block-size change. You can use this opportunity to find out the sample rate and block size that the device is going to use by calling the AudioIODevice::getCurrentSampleRate() and AudioIODevice::getCurrentBufferSizeSamples() on the supplied pointer. @param device the audio IO device that will be used to drive the callback. Note that if you're going to store this this pointer, it is only valid until the next time that audioDeviceStopped is called. */ virtual void audioDeviceAboutToStart (AudioIODevice* device) = 0; /** Called to indicate that the device has stopped. */ virtual void audioDeviceStopped() = 0; /** This can be overridden to be told if the device generates an error while operating. Be aware that this could be called by any thread! And not all devices perform this callback. */ virtual void audioDeviceError (const String& errorMessage); }; //============================================================================== /** Base class for an audio device with synchronised input and output channels. Subclasses of this are used to implement different protocols such as DirectSound, ASIO, CoreAudio, etc. To create one of these, you'll need to use the AudioIODeviceType class - see the documentation for that class for more info. For an easier way of managing audio devices and their settings, have a look at the AudioDeviceManager class. @see AudioIODeviceType, AudioDeviceManager */ class JUCE_API AudioIODevice { public: /** Destructor. */ virtual ~AudioIODevice(); //============================================================================== /** Returns the device's name, (as set in the constructor). */ const String& getName() const noexcept { return name; } /** Returns the type of the device. E.g. "CoreAudio", "ASIO", etc. - this comes from the AudioIODeviceType that created it. */ const String& getTypeName() const noexcept { return typeName; } //============================================================================== /** Returns the names of all the available output channels on this device. To find out which of these are currently in use, call getActiveOutputChannels(). */ virtual StringArray getOutputChannelNames() = 0; /** Returns the names of all the available input channels on this device. To find out which of these are currently in use, call getActiveInputChannels(). */ virtual StringArray getInputChannelNames() = 0; //============================================================================== /** Returns the set of sample-rates this device supports. @see getCurrentSampleRate */ virtual Array getAvailableSampleRates() = 0; /** Returns the set of buffer sizes that are available. @see getCurrentBufferSizeSamples, getDefaultBufferSize */ virtual Array getAvailableBufferSizes() = 0; /** Returns the default buffer-size to use. @returns a number of samples @see getAvailableBufferSizes */ virtual int getDefaultBufferSize() = 0; //============================================================================== /** Tries to open the device ready to play. @param inputChannels a BigInteger in which a set bit indicates that the corresponding input channel should be enabled @param outputChannels a BigInteger in which a set bit indicates that the corresponding output channel should be enabled @param sampleRate the sample rate to try to use - to find out which rates are available, see getAvailableSampleRates() @param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer sizes, see getAvailableBufferSizes() @returns an error description if there's a problem, or an empty string if it succeeds in opening the device @see close */ virtual String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) = 0; /** Closes and releases the device if it's open. */ virtual void close() = 0; /** Returns true if the device is still open. A device might spontaneously close itself if something goes wrong, so this checks if it's still open. */ virtual bool isOpen() = 0; /** Starts the device actually playing. This must be called after the device has been opened. @param callback the callback to use for streaming the data. @see AudioIODeviceCallback, open */ virtual void start (AudioIODeviceCallback* callback) = 0; /** Stops the device playing. Once a device has been started, this will stop it. Any pending calls to the callback class will be flushed before this method returns. */ virtual void stop() = 0; /** Returns true if the device is still calling back. The device might mysteriously stop, so this checks whether it's still playing. */ virtual bool isPlaying() = 0; /** Returns the last error that happened if anything went wrong. */ virtual String getLastError() = 0; //============================================================================== /** Returns the buffer size that the device is currently using. If the device isn't actually open, this value doesn't really mean much. */ virtual int getCurrentBufferSizeSamples() = 0; /** Returns the sample rate that the device is currently using. If the device isn't actually open, this value doesn't really mean much. */ virtual double getCurrentSampleRate() = 0; /** Returns the device's current physical bit-depth. If the device isn't actually open, this value doesn't really mean much. */ virtual int getCurrentBitDepth() = 0; /** Returns a mask showing which of the available output channels are currently enabled. @see getOutputChannelNames */ virtual BigInteger getActiveOutputChannels() const = 0; /** Returns a mask showing which of the available input channels are currently enabled. @see getInputChannelNames */ virtual BigInteger getActiveInputChannels() const = 0; /** Returns the device's output latency. This is the delay in samples between a callback getting a block of data, and that data actually getting played. */ virtual int getOutputLatencyInSamples() = 0; /** Returns the device's input latency. This is the delay in samples between some audio actually arriving at the soundcard, and the callback getting passed this block of data. */ virtual int getInputLatencyInSamples() = 0; //============================================================================== /** True if this device can show a pop-up control panel for editing its settings. This is generally just true of ASIO devices. If true, you can call showControlPanel() to display it. */ virtual bool hasControlPanel() const; /** Shows a device-specific control panel if there is one. This should only be called for devices which return true from hasControlPanel(). */ virtual bool showControlPanel(); /** On devices which support it, this allows automatic gain control or other mic processing to be disabled. If the device doesn't support this operation, it'll return false. */ virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled); //============================================================================== protected: /** Creates a device, setting its name and type member variables. */ AudioIODevice (const String& deviceName, const String& typeName); /** @internal */ String name, typeName; }; #endif // JUCE_AUDIOIODEVICE_H_INCLUDED juce_AudioIODeviceType.cpp000066400000000000000000000053761320201440200341260ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioIODeviceType::AudioIODeviceType (const String& name) : typeName (name) { } AudioIODeviceType::~AudioIODeviceType() { } //============================================================================== void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); } void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); } void AudioIODeviceType::callDeviceChangeListeners() { listeners.call (&AudioIODeviceType::Listener::audioDeviceListChanged); } //============================================================================== #if ! JUCE_MAC AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } #endif #if ! JUCE_IOS AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } #endif #if ! (JUCE_WINDOWS && JUCE_WASAPI) AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } #endif #if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND) AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } #endif #if ! (JUCE_WINDOWS && JUCE_ASIO) AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } #endif #if ! (JUCE_LINUX && JUCE_ALSA) AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } #endif #if ! (JUCE_LINUX && JUCE_JACK) AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } #endif #if ! JUCE_ANDROID AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } #endif #if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES) AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h000066400000000000000000000172041320201440200336430ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOIODEVICETYPE_H_INCLUDED #define JUCE_AUDIOIODEVICETYPE_H_INCLUDED //============================================================================== /** Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc. To get a list of available audio driver types, use the AudioDeviceManager::createAudioDeviceTypes() method. Each of the objects returned can then be used to list the available devices of that type. E.g. @code OwnedArray types; myAudioDeviceManager.createAudioDeviceTypes (types); for (int i = 0; i < types.size(); ++i) { String typeName (types[i]->getTypeName()); // This will be things like "DirectSound", "CoreAudio", etc. types[i]->scanForDevices(); // This must be called before getting the list of devices StringArray deviceNames (types[i]->getDeviceNames()); // This will now return a list of available devices of this type for (int j = 0; j < deviceNames.size(); ++j) { AudioIODevice* device = types[i]->createDevice (deviceNames [j]); ... } } @endcode For an easier way of managing audio devices and their settings, have a look at the AudioDeviceManager class. @see AudioIODevice, AudioDeviceManager */ class JUCE_API AudioIODeviceType { public: //============================================================================== /** Returns the name of this type of driver that this object manages. This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc. */ const String& getTypeName() const noexcept { return typeName; } //============================================================================== /** Refreshes the object's cached list of known devices. This must be called at least once before calling getDeviceNames() or any of the other device creation methods. */ virtual void scanForDevices() = 0; /** Returns the list of available devices of this type. The scanForDevices() method must have been called to create this list. @param wantInputNames only really used by DirectSound where devices are split up into inputs and outputs, this indicates whether to use the input or output name to refer to a pair of devices. */ virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0; /** Returns the name of the default device. This will be one of the names from the getDeviceNames() list. @param forInput if true, this means that a default input device should be returned; if false, it should return the default output */ virtual int getDefaultDeviceIndex (bool forInput) const = 0; /** Returns the index of a given device in the list of device names. If asInput is true, it shows the index in the inputs list, otherwise it looks for it in the outputs list. */ virtual int getIndexOfDevice (AudioIODevice* device, bool asInput) const = 0; /** Returns true if two different devices can be used for the input and output. */ virtual bool hasSeparateInputsAndOutputs() const = 0; /** Creates one of the devices of this type. The deviceName must be one of the strings returned by getDeviceNames(), and scanForDevices() must have been called before this method is used. */ virtual AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) = 0; //============================================================================== /** A class for receiving events when audio devices are inserted or removed. You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object using the AudioIODeviceType::addListener() method, and it will be called when devices of that type are added or removed. @see AudioIODeviceType::addListener, AudioIODeviceType::removeListener */ class Listener { public: virtual ~Listener() {} /** Called when the list of available audio devices changes. */ virtual void audioDeviceListChanged() = 0; }; /** Adds a listener that will be called when this type of device is added or removed from the system. */ void addListener (Listener* listener); /** Removes a listener that was previously added with addListener(). */ void removeListener (Listener* listener); //============================================================================== /** Destructor. */ virtual ~AudioIODeviceType(); //============================================================================== /** Creates a CoreAudio device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_CoreAudio(); /** Creates an iOS device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_iOSAudio(); /** Creates a WASAPI device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode); /** Creates a DirectSound device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_DirectSound(); /** Creates an ASIO device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_ASIO(); /** Creates an ALSA device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_ALSA(); /** Creates a JACK device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_JACK(); /** Creates an Android device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_Android(); /** Creates an Android OpenSLES device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_OpenSLES(); protected: explicit AudioIODeviceType (const String& typeName); /** Synchronously calls all the registered device list change listeners. */ void callDeviceChangeListeners(); private: String typeName; ListenerList listeners; JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType) }; #endif // JUCE_AUDIOIODEVICETYPE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h000066400000000000000000000043061320201440200340250ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED #define JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED //============================================================================== /** Contains functions to control the system's master volume. */ class JUCE_API SystemAudioVolume { public: //============================================================================== /** Returns the operating system's current volume level in the range 0 to 1.0 */ static float JUCE_CALLTYPE getGain(); /** Attempts to set the operating system's current volume level. @param newGain the level, between 0 and 1.0 @returns true if the operation succeeds */ static bool JUCE_CALLTYPE setGain (float newGain); /** Returns true if the system's audio output is currently muted. */ static bool JUCE_CALLTYPE isMuted(); /** Attempts to mute the operating system's audio output. @param shouldBeMuted true if you want it to be muted @returns true if the operation succeeds */ static bool JUCE_CALLTYPE setMuted (bool shouldBeMuted); private: SystemAudioVolume(); // Don't instantiate this class, just call its static fns. JUCE_DECLARE_NON_COPYABLE (SystemAudioVolume) }; #endif // JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp000066400000000000000000000173521320201440200317420ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if defined (JUCE_AUDIO_DEVICES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_audio_devices.h" //============================================================================== #if JUCE_MAC #define Point CarbonDummyPointName #define Component CarbonDummyCompName #import #import #import #import #undef Point #undef Component #elif JUCE_IOS #import #import #import //============================================================================== #elif JUCE_WINDOWS #if JUCE_WASAPI #include #endif #if JUCE_ASIO /* This is very frustrating - we only need to use a handful of definitions from a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy about 30 lines of code into this cpp file to create a fully stand-alone ASIO implementation... ..unfortunately that would break Steinberg's license agreement for use of their SDK, so I'm not allowed to do this. This means that anyone who wants to use JUCE's ASIO abilities will have to: 1) Agree to Steinberg's licensing terms and download the ASIO SDK (see http://www.steinberg.net/en/company/developers.html). 2) Enable this code with a global definition #define JUCE_ASIO 1. 3) Make sure that your header search path contains the iasiodrv.h file that comes with the SDK. (Only about a handful of the SDK header files are actually needed - so to simplify things, you could just copy these into your JUCE directory). */ #include #endif #if JUCE_USE_CDBURNER /* You'll need the Platform SDK for these headers - if you don't have it and don't need to use CD-burning, then you might just want to set the JUCE_USE_CDBURNER flag to 0, to avoid these includes. */ #include #include #endif //============================================================================== #elif JUCE_LINUX #if JUCE_ALSA /* Got an include error here? If so, you've either not got ALSA installed, or you've not got your paths set up correctly to find its header files. The package you need to install to get ASLA support is "libasound2-dev". If you don't have the ALSA library and don't want to build Juce with audio support, just set the JUCE_ALSA flag to 0. */ #include #endif #if JUCE_JACK /* Got an include error here? If so, you've either not got jack-audio-connection-kit installed, or you've not got your paths set up correctly to find its header files. The package you need to install to get JACK support is "libjack-dev". If you don't have the jack-audio-connection-kit library and don't want to build Juce with low latency audio support, just set the JUCE_JACK flag to 0. */ #include #endif #undef SIZEOF //============================================================================== #elif JUCE_ANDROID #if JUCE_USE_ANDROID_OPENSLES #include #include #include #endif #endif namespace juce { #include "audio_io/juce_AudioDeviceManager.cpp" #include "audio_io/juce_AudioIODevice.cpp" #include "audio_io/juce_AudioIODeviceType.cpp" #include "midi_io/juce_MidiMessageCollector.cpp" #include "midi_io/juce_MidiOutput.cpp" #include "audio_cd/juce_AudioCDReader.cpp" #include "sources/juce_AudioSourcePlayer.cpp" #include "sources/juce_AudioTransportSource.cpp" #include "native/juce_MidiDataConcatenator.h" //============================================================================== #if JUCE_MAC #include "../juce_core/native/juce_osx_ObjCHelpers.h" #include "native/juce_mac_CoreAudio.cpp" #include "native/juce_mac_CoreMidi.cpp" #if JUCE_USE_CDREADER #include "native/juce_mac_AudioCDReader.mm" #endif #if JUCE_USE_CDBURNER #include "native/juce_mac_AudioCDBurner.mm" #endif //============================================================================== #elif JUCE_IOS #include "native/juce_ios_Audio.cpp" #include "native/juce_mac_CoreMidi.cpp" //============================================================================== #elif JUCE_WINDOWS #include "../juce_core/native/juce_win32_ComSmartPtr.h" #include "../juce_events/native/juce_win32_HiddenMessageWindow.h" #if JUCE_WASAPI #include "native/juce_win32_WASAPI.cpp" #endif #if JUCE_DIRECTSOUND #include "native/juce_win32_DirectSound.cpp" #endif #include "native/juce_win32_Midi.cpp" #if JUCE_ASIO #include "native/juce_win32_ASIO.cpp" #endif #if JUCE_USE_CDREADER #include "native/juce_win32_AudioCDReader.cpp" #endif #if JUCE_USE_CDBURNER #include "native/juce_win32_AudioCDBurner.cpp" #endif //============================================================================== #elif JUCE_LINUX #if JUCE_ALSA #include "native/juce_linux_ALSA.cpp" #endif #include "native/juce_linux_Midi.cpp" #if JUCE_JACK #include "native/juce_linux_JackAudio.cpp" #endif #if JUCE_USE_CDREADER #include "native/juce_linux_AudioCDReader.cpp" #endif //============================================================================== #elif JUCE_ANDROID #include "../juce_core/native/juce_android_JNIHelpers.h" #include "native/juce_android_Audio.cpp" #include "native/juce_android_Midi.cpp" #if JUCE_USE_ANDROID_OPENSLES #include "native/juce_android_OpenSL.cpp" #endif #endif #if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED // None of these methods are available. (On Windows you might need to enable WASAPI for this) float JUCE_CALLTYPE SystemAudioVolume::getGain() { jassertfalse; return 0.0f; } bool JUCE_CALLTYPE SystemAudioVolume::setGain (float) { jassertfalse; return false; } bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { jassertfalse; return false; } bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; } #endif } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h000066400000000000000000000071441320201440200314050ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIO_DEVICES_H_INCLUDED #define JUCE_AUDIO_DEVICES_H_INCLUDED #include "../juce_events/juce_events.h" #include "../juce_audio_basics/juce_audio_basics.h" #include "../juce_audio_formats/juce_audio_formats.h" //============================================================================= /** Config: JUCE_ASIO Enables ASIO audio devices (MS Windows only). Turning this on means that you'll need to have the Steinberg ASIO SDK installed on your Windows build machine. See the comments in the ASIOAudioIODevice class's header file for more info about this. */ #ifndef JUCE_ASIO #define JUCE_ASIO 0 #endif /** Config: JUCE_WASAPI Enables WASAPI audio devices (Windows Vista and above). See also the JUCE_WASAPI_EXCLUSIVE flag. */ #ifndef JUCE_WASAPI #define JUCE_WASAPI 1 #endif /** Config: JUCE_WASAPI_EXCLUSIVE Enables WASAPI audio devices in exclusive mode (Windows Vista and above). */ #ifndef JUCE_WASAPI_EXCLUSIVE #define JUCE_WASAPI_EXCLUSIVE 1 #endif /** Config: JUCE_DIRECTSOUND Enables DirectSound audio (MS Windows only). */ #ifndef JUCE_DIRECTSOUND #define JUCE_DIRECTSOUND 1 #endif /** Config: JUCE_ALSA Enables ALSA audio devices (Linux only). */ #ifndef JUCE_ALSA #define JUCE_ALSA 1 #endif /** Config: JUCE_JACK Enables JACK audio devices (Linux only). */ #ifndef JUCE_JACK #define JUCE_JACK 0 #endif /** Config: JUCE_USE_ANDROID_OPENSLES Enables OpenSLES devices (Android only). */ #ifndef JUCE_USE_ANDROID_OPENSLES #if JUCE_ANDROID_API_VERSION > 8 #define JUCE_USE_ANDROID_OPENSLES 1 #else #define JUCE_USE_ANDROID_OPENSLES 0 #endif #endif //============================================================================= /** Config: JUCE_USE_CDREADER Enables the AudioCDReader class (on supported platforms). */ #ifndef JUCE_USE_CDREADER #define JUCE_USE_CDREADER 0 #endif /** Config: JUCE_USE_CDBURNER Enables the AudioCDBurner class (on supported platforms). */ #ifndef JUCE_USE_CDBURNER #define JUCE_USE_CDBURNER 0 #endif //============================================================================= namespace juce { #include "audio_io/juce_AudioIODevice.h" #include "audio_io/juce_AudioIODeviceType.h" #include "audio_io/juce_SystemAudioVolume.h" #include "midi_io/juce_MidiInput.h" #include "midi_io/juce_MidiMessageCollector.h" #include "midi_io/juce_MidiOutput.h" #include "sources/juce_AudioSourcePlayer.h" #include "sources/juce_AudioTransportSource.h" #include "audio_cd/juce_AudioCDBurner.h" #include "audio_cd/juce_AudioCDReader.h" #include "audio_io/juce_AudioDeviceManager.h" } #endif // JUCE_AUDIO_DEVICES_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm000066400000000000000000000017061320201440200315650ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_audio_devices.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/juce_module_info000066400000000000000000000021331320201440200306450ustar00rootroot00000000000000{ "id": "juce_audio_devices", "name": "JUCE audio and midi I/O device classes", "version": "3.2.0", "description": "Classes to play and record from audio and midi i/o devices.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", "dependencies": [ { "id": "juce_audio_basics", "version": "matching" }, { "id": "juce_audio_formats", "version": "matching" }, { "id": "juce_events", "version": "matching" } ], "include": "juce_audio_devices.h", "compile": [ { "file": "juce_audio_devices.cpp", "target": "! xcode" }, { "file": "juce_audio_devices.mm", "target": "xcode" } ], "browse": [ "audio_io/*", "midi_io/*", "sources/*", "audio_cd/*", "native/*" ], "OSXFrameworks": "CoreAudio CoreMIDI DiscRecording", "iOSFrameworks": "AudioToolbox CoreMIDI", "LinuxLibs": "asound", "mingwLibs": "winmm" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/midi_io/000077500000000000000000000000001320201440200270265ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h000066400000000000000000000150431320201440200321120ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIINPUT_H_INCLUDED #define JUCE_MIDIINPUT_H_INCLUDED class MidiInput; //============================================================================== /** Receives incoming messages from a physical MIDI input device. This class is overridden to handle incoming midi messages. See the MidiInput class for more details. @see MidiInput */ class JUCE_API MidiInputCallback { public: /** Destructor. */ virtual ~MidiInputCallback() {} /** Receives an incoming message. A MidiInput object will call this method when a midi event arrives. It'll be called on a high-priority system thread, so avoid doing anything time-consuming in here, and avoid making any UI calls. You might find the MidiBuffer class helpful for queueing incoming messages for use later. @param source the MidiInput object that generated the message @param message the incoming message. The message's timestamp is set to a value equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the time when the message arrived. */ virtual void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) = 0; /** Notification sent each time a packet of a multi-packet sysex message arrives. If a long sysex message is broken up into multiple packets, this callback is made for each packet that arrives until the message is finished, at which point the normal handleIncomingMidiMessage() callback will be made with the entire message. The message passed in will contain the start of a sysex, but won't be finished with the terminating 0xf7 byte. */ virtual void handlePartialSysexMessage (MidiInput* source, const uint8* messageData, int numBytesSoFar, double timestamp) { // (this bit is just to avoid compiler warnings about unused variables) (void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp; } }; //============================================================================== /** Represents a midi input device. To create one of these, use the static getDevices() method to find out what inputs are available, and then use the openDevice() method to try to open one. @see MidiOutput */ class JUCE_API MidiInput { public: //============================================================================== /** Returns a list of the available midi input devices. You can open one of the devices by passing its index into the openDevice() method. @see getDefaultDeviceIndex, openDevice */ static StringArray getDevices(); /** Returns the index of the default midi input device to use. This refers to the index in the list returned by getDevices(). */ static int getDefaultDeviceIndex(); /** Tries to open one of the midi input devices. This will return a MidiInput object if it manages to open it. You can then call start() and stop() on this device, and delete it when no longer needed. If the device can't be opened, this will return a null pointer. @param deviceIndex the index of a device from the list returned by getDevices() @param callback the object that will receive the midi messages from this device. @see MidiInputCallback, getDevices */ static MidiInput* openDevice (int deviceIndex, MidiInputCallback* callback); #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN /** This will try to create a new midi input device (Not available on Windows). This will attempt to create a new midi input device with the specified name, for other apps to connect to. Returns nullptr if a device can't be created. @param deviceName the name to use for the new device @param callback the object that will receive the midi messages from this device. */ static MidiInput* createNewDevice (const String& deviceName, MidiInputCallback* callback); #endif //============================================================================== /** Destructor. */ ~MidiInput(); /** Returns the name of this device. */ const String& getName() const noexcept { return name; } /** Allows you to set a custom name for the device, in case you don't like the name it was given when created. */ void setName (const String& newName) noexcept { name = newName; } //============================================================================== /** Starts the device running. After calling this, the device will start sending midi messages to the MidiInputCallback object that was specified when the openDevice() method was called. @see stop */ void start(); /** Stops the device running. @see start */ void stop(); private: //============================================================================== String name; void* internal; // The input objects are created with the openDevice() method. explicit MidiInput (const String&); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) }; #endif // JUCE_MIDIINPUT_H_INCLUDED juce_MidiMessageCollector.cpp000066400000000000000000000124751320201440200345300ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/midi_io/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ MidiMessageCollector::MidiMessageCollector() : lastCallbackTime (0), sampleRate (44100.0001) { } MidiMessageCollector::~MidiMessageCollector() { } //============================================================================== void MidiMessageCollector::reset (const double sampleRate_) { jassert (sampleRate_ > 0); const ScopedLock sl (midiCallbackLock); sampleRate = sampleRate_; incomingMessages.clear(); lastCallbackTime = Time::getMillisecondCounterHiRes(); } void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) { // you need to call reset() to set the correct sample rate before using this object jassert (sampleRate != 44100.0001); // the messages that come in here need to be time-stamped correctly - see MidiInput // for details of what the number should be. jassert (message.getTimeStamp() != 0); const ScopedLock sl (midiCallbackLock); const int sampleNumber = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate); incomingMessages.addEvent (message, sampleNumber); // if the messages don't get used for over a second, we'd better // get rid of any old ones to avoid the queue getting too big if (sampleNumber > sampleRate) incomingMessages.clear (0, sampleNumber - (int) sampleRate); } void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, const int numSamples) { // you need to call reset() to set the correct sample rate before using this object jassert (sampleRate != 44100.0001); jassert (numSamples > 0); const double timeNow = Time::getMillisecondCounterHiRes(); const double msElapsed = timeNow - lastCallbackTime; const ScopedLock sl (midiCallbackLock); lastCallbackTime = timeNow; if (! incomingMessages.isEmpty()) { int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate)); int startSample = 0; int scale = 1 << 16; const uint8* midiData; int numBytes, samplePosition; MidiBuffer::Iterator iter (incomingMessages); if (numSourceSamples > numSamples) { // if our list of events is longer than the buffer we're being // asked for, scale them down to squeeze them all in.. const int maxBlockLengthToUse = numSamples << 5; if (numSourceSamples > maxBlockLengthToUse) { startSample = numSourceSamples - maxBlockLengthToUse; numSourceSamples = maxBlockLengthToUse; iter.setNextSamplePosition (startSample); } scale = (numSamples << 10) / numSourceSamples; while (iter.getNextEvent (midiData, numBytes, samplePosition)) { samplePosition = ((samplePosition - startSample) * scale) >> 10; destBuffer.addEvent (midiData, numBytes, jlimit (0, numSamples - 1, samplePosition)); } } else { // if our event list is shorter than the number we need, put them // towards the end of the buffer startSample = numSamples - numSourceSamples; while (iter.getNextEvent (midiData, numBytes, samplePosition)) { destBuffer.addEvent (midiData, numBytes, jlimit (0, numSamples - 1, samplePosition + startSample)); } } incomingMessages.clear(); } } //============================================================================== void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) { MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity)); m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001); addMessageToQueue (m); } void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) { MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber)); m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001); addMessageToQueue (m); } void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) { addMessageToQueue (message); } juce_MidiMessageCollector.h000066400000000000000000000076511320201440200341750ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/midi_io/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED #define JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED //============================================================================== /** Collects incoming realtime MIDI messages and turns them into blocks suitable for processing by a block-based audio callback. The class can also be used as either a MidiKeyboardStateListener or a MidiInputCallback so it can easily use a midi input or keyboard component as its source. @see MidiMessage, MidiInput */ class JUCE_API MidiMessageCollector : public MidiKeyboardStateListener, public MidiInputCallback { public: //============================================================================== /** Creates a MidiMessageCollector. */ MidiMessageCollector(); /** Destructor. */ ~MidiMessageCollector(); //============================================================================== /** Clears any messages from the queue. You need to call this method before starting to use the collector, so that it knows the correct sample rate to use. */ void reset (double sampleRate); /** Takes an incoming real-time message and adds it to the queue. The message's timestamp is taken, and it will be ready for retrieval as part of the block returned by the next call to removeNextBlockOfMessages(). This method is fully thread-safe when overlapping calls are made with removeNextBlockOfMessages(). */ void addMessageToQueue (const MidiMessage& message); /** Removes all the pending messages from the queue as a buffer. This will also correct the messages' timestamps to make sure they're in the range 0 to numSamples - 1. This call should be made regularly by something like an audio processing callback, because the time that it happens is used in calculating the midi event positions. This method is fully thread-safe when overlapping calls are made with addMessageToQueue(). Precondition: numSamples must be greater than 0. */ void removeNextBlockOfMessages (MidiBuffer& destBuffer, int numSamples); //============================================================================== /** @internal */ void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override; /** @internal */ void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) override; /** @internal */ void handleIncomingMidiMessage (MidiInput*, const MidiMessage&) override; private: //============================================================================== double lastCallbackTime; CriticalSection midiCallbackLock; MidiBuffer incomingMessages; double sampleRate; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiMessageCollector) }; #endif // JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp000066400000000000000000000104541320201440200326470ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct MidiOutput::PendingMessage { PendingMessage (const void* const data, const int len, const double timeStamp) : message (data, len, timeStamp) {} MidiMessage message; PendingMessage* next; }; MidiOutput::MidiOutput() : Thread ("midi out"), internal (nullptr), firstMessage (nullptr) { } void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer, const double millisecondCounterToStartAt, double samplesPerSecondForBuffer) { // You've got to call startBackgroundThread() for this to actually work.. jassert (isThreadRunning()); // this needs to be a value in the future - RTFM for this method! jassert (millisecondCounterToStartAt > 0); const double timeScaleFactor = 1000.0 / samplesPerSecondForBuffer; MidiBuffer::Iterator i (buffer); const uint8* data; int len, time; while (i.getNextEvent (data, len, time)) { const double eventTime = millisecondCounterToStartAt + timeScaleFactor * time; PendingMessage* const m = new PendingMessage (data, len, eventTime); const ScopedLock sl (lock); if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime) { m->next = firstMessage; firstMessage = m; } else { PendingMessage* mm = firstMessage; while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime) mm = mm->next; m->next = mm->next; mm->next = m; } } notify(); } void MidiOutput::clearAllPendingMessages() { const ScopedLock sl (lock); while (firstMessage != nullptr) { PendingMessage* const m = firstMessage; firstMessage = firstMessage->next; delete m; } } void MidiOutput::startBackgroundThread() { startThread (9); } void MidiOutput::stopBackgroundThread() { stopThread (5000); } void MidiOutput::run() { while (! threadShouldExit()) { uint32 now = Time::getMillisecondCounter(); uint32 eventTime = 0; uint32 timeToWait = 500; PendingMessage* message; { const ScopedLock sl (lock); message = firstMessage; if (message != nullptr) { eventTime = (uint32) roundToInt (message->message.getTimeStamp()); if (eventTime > now + 20) { timeToWait = eventTime - (now + 20); message = nullptr; } else { firstMessage = message->next; } } } if (message != nullptr) { const ScopedPointer messageDeleter (message); if (eventTime > now) { Time::waitForMillisecondCounter (eventTime); if (threadShouldExit()) break; } if (eventTime > now - 200) sendMessageNow (message->message); } else { jassert (timeToWait < 1000 * 30); wait ((int) timeToWait); } } clearAllPendingMessages(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h000066400000000000000000000121761320201440200323170ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIOUTPUT_H_INCLUDED #define JUCE_MIDIOUTPUT_H_INCLUDED //============================================================================== /** Controls a physical MIDI output device. To create one of these, use the static getDevices() method to get a list of the available output devices, then use the openDevice() method to try to open one. @see MidiInput */ class JUCE_API MidiOutput : private Thread { public: //============================================================================== /** Returns a list of the available midi output devices. You can open one of the devices by passing its index into the openDevice() method. @see getDefaultDeviceIndex, openDevice */ static StringArray getDevices(); /** Returns the index of the default midi output device to use. This refers to the index in the list returned by getDevices(). */ static int getDefaultDeviceIndex(); /** Tries to open one of the midi output devices. This will return a MidiOutput object if it manages to open it. You can then send messages to this device, and delete it when no longer needed. If the device can't be opened, this will return a null pointer. @param deviceIndex the index of a device from the list returned by getDevices() @see getDevices */ static MidiOutput* openDevice (int deviceIndex); #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN /** This will try to create a new midi output device (Not available on Windows). This will attempt to create a new midi output device that other apps can connect to and use as their midi input. Returns nullptr if a device can't be created. @param deviceName the name to use for the new device */ static MidiOutput* createNewDevice (const String& deviceName); #endif //============================================================================== /** Destructor. */ ~MidiOutput(); /** Makes this device output a midi message. @see MidiMessage */ void sendMessageNow (const MidiMessage& message); //============================================================================== /** This lets you supply a block of messages that will be sent out at some point in the future. The MidiOutput class has an internal thread that can send out timestamped messages - this appends a set of messages to its internal buffer, ready for sending. This will only work if you've already started the thread with startBackgroundThread(). A time is specified, at which the block of messages should be sent. This time uses the same time base as Time::getMillisecondCounter(), and must be in the future. The samplesPerSecondForBuffer parameter indicates the number of samples per second used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the samplesPerSecondForBuffer value is needed to convert this sample position to a real time. */ void sendBlockOfMessages (const MidiBuffer& buffer, double millisecondCounterToStartAt, double samplesPerSecondForBuffer); /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). */ void clearAllPendingMessages(); /** Starts up a background thread so that the device can send blocks of data. Call this to get the device ready, before using sendBlockOfMessages(). */ void startBackgroundThread(); /** Stops the background thread, and clears any pending midi events. @see startBackgroundThread */ void stopBackgroundThread(); private: //============================================================================== void* internal; CriticalSection lock; struct PendingMessage; PendingMessage* firstMessage; MidiOutput(); // These objects are created with the openDevice() method. void run() override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput) }; #endif // JUCE_MIDIOUTPUT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/000077500000000000000000000000001320201440200267035ustar00rootroot00000000000000juce_MidiDataConcatenator.h000066400000000000000000000136661320201440200340340ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MIDIDATACONCATENATOR_H_INCLUDED #define JUCE_MIDIDATACONCATENATOR_H_INCLUDED //============================================================================== /** Helper class that takes chunks of incoming midi bytes, packages them into messages, and dispatches them to a midi callback. */ class MidiDataConcatenator { public: //============================================================================== MidiDataConcatenator (const int initialBufferSize) : pendingData ((size_t) initialBufferSize), pendingDataTime (0), pendingBytes (0), runningStatus (0) { } void reset() { pendingBytes = 0; runningStatus = 0; pendingDataTime = 0; } template void pushMidiData (const void* inputData, int numBytes, double time, UserDataType* input, CallbackType& callback) { const uint8* d = static_cast (inputData); while (numBytes > 0) { if (pendingBytes > 0 || d[0] == 0xf0) { processSysex (d, numBytes, time, input, callback); runningStatus = 0; } else { int len = 0; uint8 data[3]; while (numBytes > 0) { // If there's a realtime message embedded in the middle of // the normal message, handle it now.. if (*d >= 0xf8 && *d <= 0xfe) { const MidiMessage m (*d++, time); callback.handleIncomingMidiMessage (input, m); --numBytes; } else { if (len == 0 && *d < 0x80 && runningStatus >= 0x80) data[len++] = runningStatus; data[len++] = *d++; --numBytes; if (len >= MidiMessage::getMessageLengthFromFirstByte (data[0])) break; } } if (len > 0) { int used = 0; const MidiMessage m (data, len, used, 0, time); if (used <= 0) break; // malformed message.. jassert (used == len); callback.handleIncomingMidiMessage (input, m); runningStatus = data[0]; } } } } private: template void processSysex (const uint8*& d, int& numBytes, double time, UserDataType* input, CallbackType& callback) { if (*d == 0xf0) { pendingBytes = 0; pendingDataTime = time; } pendingData.ensureSize ((size_t) (pendingBytes + numBytes), false); uint8* totalMessage = static_cast (pendingData.getData()); uint8* dest = totalMessage + pendingBytes; do { if (pendingBytes > 0 && *d >= 0x80) { if (*d == 0xf7) { *dest++ = *d++; ++pendingBytes; --numBytes; break; } if (*d >= 0xfa || *d == 0xf8) { callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); ++d; --numBytes; } else { pendingBytes = 0; int used = 0; const MidiMessage m (d, numBytes, used, 0, time); if (used > 0) { callback.handleIncomingMidiMessage (input, m); numBytes -= used; d += used; } break; } } else { *dest++ = *d++; ++pendingBytes; --numBytes; } } while (numBytes > 0); if (pendingBytes > 0) { if (totalMessage [pendingBytes - 1] == 0xf7) { callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); pendingBytes = 0; } else { callback.handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); } } } MemoryBlock pendingData; double pendingDataTime; int pendingBytes; uint8 runningStatus; JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator) }; #endif // JUCE_MIDIDATACONCATENATOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp000066400000000000000000000417451320201440200331710ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \ STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \ METHOD (constructor, "", "(IIIIII)V") \ METHOD (getState, "getState", "()I") \ METHOD (play, "play", "()V") \ METHOD (stop, "stop", "()V") \ METHOD (release, "release", "()V") \ METHOD (flush, "flush", "()V") \ METHOD (write, "write", "([SII)I") \ DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack"); #undef JNI_CLASS_MEMBERS //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \ METHOD (constructor, "", "(IIIII)V") \ METHOD (getState, "getState", "()I") \ METHOD (startRecording, "startRecording", "()V") \ METHOD (stop, "stop", "()V") \ METHOD (read, "read", "([SII)I") \ METHOD (release, "release", "()V") \ DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord"); #undef JNI_CLASS_MEMBERS //============================================================================== enum { CHANNEL_OUT_STEREO = 12, CHANNEL_IN_STEREO = 12, CHANNEL_IN_MONO = 16, ENCODING_PCM_16BIT = 2, STREAM_MUSIC = 3, MODE_STREAM = 1, STATE_UNINITIALIZED = 0 }; const char* const javaAudioTypeName = "Android Audio"; //============================================================================== class AndroidAudioIODevice : public AudioIODevice, public Thread { public: //============================================================================== AndroidAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, javaAudioTypeName), Thread ("audio"), minBufferSizeOut (0), minBufferSizeIn (0), callback (0), sampleRate (0), numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2), numClientOutputChannels (0), numDeviceOutputChannels (0), actualBufferSize (0), isRunning (false), inputChannelBuffer (1, 1), outputChannelBuffer (1, 1) { JNIEnv* env = getEnv(); sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM); minBufferSizeOut = (int) env->CallStaticIntMethod (AudioTrack, AudioTrack.getMinBufferSize, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT); minBufferSizeIn = (int) env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_STEREO, ENCODING_PCM_16BIT); if (minBufferSizeIn <= 0) { minBufferSizeIn = env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_MONO, ENCODING_PCM_16BIT); if (minBufferSizeIn > 0) numDeviceInputChannelsAvailable = 1; else numDeviceInputChannelsAvailable = 0; } DBG ("Audio device - min buffers: " << minBufferSizeOut << ", " << minBufferSizeIn << "; " << sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable); } ~AndroidAudioIODevice() { close(); } StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); s.add ("Right"); return s; } StringArray getInputChannelNames() override { StringArray s; if (numDeviceInputChannelsAvailable == 2) { s.add ("Left"); s.add ("Right"); } else if (numDeviceInputChannelsAvailable == 1) { s.add ("Audio Input"); } return s; } Array getAvailableSampleRates() override { Array r; r.add ((double) sampleRate); return r; } Array getAvailableBufferSizes() override { Array b; int n = 16; for (int i = 0; i < 50; ++i) { b.add (n); n += n < 64 ? 16 : (n < 512 ? 32 : (n < 1024 ? 64 : (n < 2048 ? 128 : 256))); } return b; } int getDefaultBufferSize() override { return 2048; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double requestedSampleRate, int bufferSize) override { close(); if (sampleRate != (int) requestedSampleRate) return "Sample rate not allowed"; lastError.clear(); int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; numDeviceInputChannels = 0; numDeviceOutputChannels = 0; activeOutputChans = outputChannels; activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); numClientOutputChannels = activeOutputChans.countNumberOfSetBits(); activeInputChans = inputChannels; activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); numClientInputChannels = activeInputChans.countNumberOfSetBits(); actualBufferSize = preferredBufferSize; inputChannelBuffer.setSize (2, actualBufferSize); inputChannelBuffer.clear(); outputChannelBuffer.setSize (2, actualBufferSize); outputChannelBuffer.clear(); JNIEnv* env = getEnv(); if (numClientOutputChannels > 0) { numDeviceOutputChannels = 2; outputDevice = GlobalRef (env->NewObject (AudioTrack, AudioTrack.constructor, STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, (jint) (minBufferSizeOut * numDeviceOutputChannels * sizeof (int16)), MODE_STREAM)); if (env->CallIntMethod (outputDevice, AudioTrack.getState) != STATE_UNINITIALIZED) isRunning = true; else outputDevice.clear(); // failed to open the device } if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0) { numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor, 0 /* (default audio source) */, sampleRate, numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, ENCODING_PCM_16BIT, (jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16)))); if (env->CallIntMethod (inputDevice, AudioRecord.getState) != STATE_UNINITIALIZED) isRunning = true; else inputDevice.clear(); // failed to open the device } if (isRunning) { if (outputDevice != nullptr) env->CallVoidMethod (outputDevice, AudioTrack.play); if (inputDevice != nullptr) env->CallVoidMethod (inputDevice, AudioRecord.startRecording); startThread (8); } else { closeDevices(); } return lastError; } void close() override { if (isRunning) { stopThread (2000); isRunning = false; closeDevices(); } } int getOutputLatencyInSamples() override { return (minBufferSizeOut * 3) / 4; } int getInputLatencyInSamples() override { return (minBufferSizeIn * 3) / 4; } bool isOpen() override { return isRunning; } int getCurrentBufferSizeSamples() override { return actualBufferSize; } int getCurrentBitDepth() override { return 16; } double getCurrentSampleRate() override { return sampleRate; } BigInteger getActiveOutputChannels() const override { return activeOutputChans; } BigInteger getActiveInputChannels() const override { return activeInputChans; } String getLastError() override { return lastError; } bool isPlaying() override { return isRunning && callback != 0; } void start (AudioIODeviceCallback* newCallback) override { if (isRunning && callback != newCallback) { if (newCallback != nullptr) newCallback->audioDeviceAboutToStart (this); const ScopedLock sl (callbackLock); callback = newCallback; } } void stop() override { if (isRunning) { AudioIODeviceCallback* lastCallback; { const ScopedLock sl (callbackLock); lastCallback = callback; callback = nullptr; } if (lastCallback != nullptr) lastCallback->audioDeviceStopped(); } } void run() override { JNIEnv* env = getEnv(); jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels)); while (! threadShouldExit()) { if (inputDevice != nullptr) { jint numRead = env->CallIntMethod (inputDevice, AudioRecord.read, audioBuffer, 0, actualBufferSize * numDeviceInputChannels); if (numRead < actualBufferSize * numDeviceInputChannels) { DBG ("Audio read under-run! " << numRead); } jshort* const src = env->GetShortArrayElements (audioBuffer, 0); for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) { AudioData::Pointer d (inputChannelBuffer.getWritePointer (chan)); if (chan < numDeviceInputChannels) { AudioData::Pointer s (src + chan, numDeviceInputChannels); d.convertSamples (s, actualBufferSize); } else { d.clearSamples (actualBufferSize); } } env->ReleaseShortArrayElements (audioBuffer, src, 0); } if (threadShouldExit()) break; { const ScopedLock sl (callbackLock); if (callback != nullptr) { callback->audioDeviceIOCallback (inputChannelBuffer.getArrayOfReadPointers(), numClientInputChannels, outputChannelBuffer.getArrayOfWritePointers(), numClientOutputChannels, actualBufferSize); } else { outputChannelBuffer.clear(); } } if (outputDevice != nullptr) { if (threadShouldExit()) break; jshort* const dest = env->GetShortArrayElements (audioBuffer, 0); for (int chan = 0; chan < numDeviceOutputChannels; ++chan) { AudioData::Pointer d (dest + chan, numDeviceOutputChannels); const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); AudioData::Pointer s (sourceChanData); d.convertSamples (s, actualBufferSize); } env->ReleaseShortArrayElements (audioBuffer, dest, 0); jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels); if (numWritten < actualBufferSize * numDeviceOutputChannels) { DBG ("Audio write underrun! " << numWritten); } } } } int minBufferSizeOut, minBufferSizeIn; private: //================================================================================================== CriticalSection callbackLock; AudioIODeviceCallback* callback; jint sampleRate; int numClientInputChannels, numDeviceInputChannels, numDeviceInputChannelsAvailable; int numClientOutputChannels, numDeviceOutputChannels; int actualBufferSize; bool isRunning; String lastError; BigInteger activeOutputChans, activeInputChans; GlobalRef outputDevice, inputDevice; AudioSampleBuffer inputChannelBuffer, outputChannelBuffer; void closeDevices() { if (outputDevice != nullptr) { outputDevice.callVoidMethod (AudioTrack.stop); outputDevice.callVoidMethod (AudioTrack.release); outputDevice.clear(); } if (inputDevice != nullptr) { inputDevice.callVoidMethod (AudioRecord.stop); inputDevice.callVoidMethod (AudioRecord.release); inputDevice.clear(); } } JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice) }; //============================================================================== class AndroidAudioIODeviceType : public AudioIODeviceType { public: AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {} //============================================================================== void scanForDevices() {} StringArray getDeviceNames (bool wantInputNames) const { return StringArray (javaAudioTypeName); } int getDefaultDeviceIndex (bool forInput) const { return 0; } int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; } bool hasSeparateInputsAndOutputs() const { return false; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { ScopedPointer dev; if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) { dev = new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName); if (dev->getCurrentSampleRate() <= 0 || dev->getDefaultBufferSize() <= 0) dev = nullptr; } return dev.release(); } private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType) }; //============================================================================== extern bool isOpenSLAvailable(); AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { #if JUCE_USE_ANDROID_OPENSLES if (isOpenSLAvailable()) return nullptr; #endif return new AndroidAudioIODeviceType(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp000066400000000000000000000034331320201440200330020ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ StringArray MidiOutput::getDevices() { StringArray devices; return devices; } int MidiOutput::getDefaultDeviceIndex() { return 0; } MidiOutput* MidiOutput::openDevice (int index) { return nullptr; } MidiOutput::~MidiOutput() { stopBackgroundThread(); } void MidiOutput::sendMessageNow (const MidiMessage&) { } //============================================================================== MidiInput::MidiInput (const String& name_) : name (name_), internal (0) { } MidiInput::~MidiInput() { } void MidiInput::start() { } void MidiInput::stop() { } int MidiInput::getDefaultDeviceIndex() { return 0; } StringArray MidiInput::getDevices() { StringArray devs; return devs; } MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { return nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp000066400000000000000000000620621320201440200332630ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ const char* const openSLTypeName = "Android OpenSL"; bool isOpenSLAvailable() { DynamicLibrary library; return library.open ("libOpenSLES.so"); } //============================================================================== class OpenSLAudioIODevice : public AudioIODevice, public Thread { public: OpenSLAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, openSLTypeName), Thread ("OpenSL"), callback (nullptr), sampleRate (0), deviceOpen (false), inputBuffer (2, 2), outputBuffer (2, 2) { // OpenSL has piss-poor support for determining latency, so the only way I can find to // get a number for this is by asking the AudioTrack/AudioRecord classes.. AndroidAudioIODevice javaDevice (String::empty); // this is a total guess about how to calculate the latency, but seems to vaguely agree // with the devices I've tested.. YMMV inputLatency = (javaDevice.minBufferSizeIn * 2) / 3; outputLatency = (javaDevice.minBufferSizeOut * 2) / 3; const int64 longestLatency = jmax (inputLatency, outputLatency); const int64 totalLatency = inputLatency + outputLatency; inputLatency = (int) ((longestLatency * inputLatency) / totalLatency) & ~15; outputLatency = (int) ((longestLatency * outputLatency) / totalLatency) & ~15; } ~OpenSLAudioIODevice() { close(); } bool openedOk() const { return engine.outputMixObject != nullptr; } StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); s.add ("Right"); return s; } StringArray getInputChannelNames() override { StringArray s; s.add ("Audio Input"); return s; } Array getAvailableSampleRates() override { static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 }; return Array (rates, numElementsInArray (rates)); } Array getAvailableBufferSizes() override { static const int sizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size return Array (sizes, numElementsInArray (sizes)); } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double requestedSampleRate, int bufferSize) override { close(); lastError.clear(); sampleRate = (int) requestedSampleRate; int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; activeOutputChans = outputChannels; activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); numOutputChannels = activeOutputChans.countNumberOfSetBits(); activeInputChans = inputChannels; activeInputChans.setRange (1, activeInputChans.getHighestBit(), false); numInputChannels = activeInputChans.countNumberOfSetBits(); actualBufferSize = preferredBufferSize; inputBuffer.setSize (jmax (1, numInputChannels), actualBufferSize); outputBuffer.setSize (jmax (1, numOutputChannels), actualBufferSize); outputBuffer.clear(); recorder = engine.createRecorder (numInputChannels, sampleRate); player = engine.createPlayer (numOutputChannels, sampleRate); startThread (8); deviceOpen = true; return lastError; } void close() override { stop(); stopThread (6000); deviceOpen = false; recorder = nullptr; player = nullptr; } int getDefaultBufferSize() override { return 1024; } int getOutputLatencyInSamples() override { return outputLatency; } int getInputLatencyInSamples() override { return inputLatency; } bool isOpen() override { return deviceOpen; } int getCurrentBufferSizeSamples() override { return actualBufferSize; } int getCurrentBitDepth() override { return 16; } double getCurrentSampleRate() override { return sampleRate; } BigInteger getActiveOutputChannels() const override { return activeOutputChans; } BigInteger getActiveInputChannels() const override { return activeInputChans; } String getLastError() override { return lastError; } bool isPlaying() override { return callback != nullptr; } void start (AudioIODeviceCallback* newCallback) override { stop(); if (deviceOpen && callback != newCallback) { if (newCallback != nullptr) newCallback->audioDeviceAboutToStart (this); setCallback (newCallback); } } void stop() override { if (AudioIODeviceCallback* const oldCallback = setCallback (nullptr)) oldCallback->audioDeviceStopped(); } bool setAudioPreprocessingEnabled (bool enable) override { return recorder != nullptr && recorder->setAudioPreprocessingEnabled (enable); } private: //================================================================================================== CriticalSection callbackLock; AudioIODeviceCallback* callback; int actualBufferSize, sampleRate; int inputLatency, outputLatency; bool deviceOpen; String lastError; BigInteger activeOutputChans, activeInputChans; int numInputChannels, numOutputChannels; AudioSampleBuffer inputBuffer, outputBuffer; struct Player; struct Recorder; AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback) { const ScopedLock sl (callbackLock); AudioIODeviceCallback* const oldCallback = callback; callback = newCallback; return oldCallback; } void run() override { if (recorder != nullptr) recorder->start(); if (player != nullptr) player->start(); while (! threadShouldExit()) { if (player != nullptr) player->writeBuffer (outputBuffer, *this); if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this); const ScopedLock sl (callbackLock); if (callback != nullptr) { callback->audioDeviceIOCallback (numInputChannels > 0 ? inputBuffer.getArrayOfReadPointers() : nullptr, numInputChannels, numOutputChannels > 0 ? outputBuffer.getArrayOfWritePointers() : nullptr, numOutputChannels, actualBufferSize); } else { outputBuffer.clear(); } } } //================================================================================================== struct Engine { Engine() : engineObject (nullptr), engineInterface (nullptr), outputMixObject (nullptr) { if (library.open ("libOpenSLES.so")) { typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*); if (CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine")) { check (createEngine (&engineObject, 0, nullptr, 0, nullptr, nullptr)); SLInterfaceID* SL_IID_ENGINE = (SLInterfaceID*) library.getFunction ("SL_IID_ENGINE"); SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY"); SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD"); SL_IID_ANDROIDCONFIGURATION = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDCONFIGURATION"); check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE)); check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface)); check ((*engineInterface)->CreateOutputMix (engineInterface, &outputMixObject, 0, nullptr, nullptr)); check ((*outputMixObject)->Realize (outputMixObject, SL_BOOLEAN_FALSE)); } } } ~Engine() { if (outputMixObject != nullptr) (*outputMixObject)->Destroy (outputMixObject); if (engineObject != nullptr) (*engineObject)->Destroy (engineObject); } Player* createPlayer (const int numChannels, const int sampleRate) { if (numChannels <= 0) return nullptr; ScopedPointer player (new Player (numChannels, sampleRate, *this)); return player->openedOk() ? player.release() : nullptr; } Recorder* createRecorder (const int numChannels, const int sampleRate) { if (numChannels <= 0) return nullptr; ScopedPointer recorder (new Recorder (numChannels, sampleRate, *this)); return recorder->openedOk() ? recorder.release() : nullptr; } SLObjectItf engineObject; SLEngineItf engineInterface; SLObjectItf outputMixObject; SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE; SLInterfaceID* SL_IID_PLAY; SLInterfaceID* SL_IID_RECORD; SLInterfaceID* SL_IID_ANDROIDCONFIGURATION; private: DynamicLibrary library; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine) }; //================================================================================================== struct BufferList { BufferList (const int numChannels_) : numChannels (numChannels_), bufferSpace (numChannels_ * numSamples * numBuffers), nextBlock (0) { } int16* waitForFreeBuffer (Thread& threadToCheck) { while (numBlocksOut.get() == numBuffers) { dataArrived.wait (1); if (threadToCheck.threadShouldExit()) return nullptr; } return getNextBuffer(); } int16* getNextBuffer() { if (++nextBlock == numBuffers) nextBlock = 0; return bufferSpace + nextBlock * numChannels * numSamples; } void bufferReturned() { --numBlocksOut; dataArrived.signal(); } void bufferSent() { ++numBlocksOut; dataArrived.signal(); } int getBufferSizeBytes() const { return numChannels * numSamples * sizeof (int16); } const int numChannels; enum { numSamples = 256, numBuffers = 16 }; private: HeapBlock bufferSpace; int nextBlock; Atomic numBlocksOut; WaitableEvent dataArrived; }; //================================================================================================== struct Player { Player (int numChannels, int sampleRate, Engine& engine) : playerObject (nullptr), playerPlay (nullptr), playerBufferQueue (nullptr), bufferList (numChannels) { jassert (numChannels == 2); SLDataFormat_PCM pcmFormat = { SL_DATAFORMAT_PCM, (SLuint32) numChannels, (SLuint32) (sampleRate * 1000), // (sample rate units are millihertz) SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN }; SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers }; SLDataSource audioSrc = { &bufferQueue, &pcmFormat }; SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, engine.outputMixObject }; SLDataSink audioSink = { &outputMix, nullptr }; // (SL_IID_BUFFERQUEUE is not guaranteed to remain future-proof, so use SL_IID_ANDROIDSIMPLEBUFFERQUEUE) const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; const SLboolean flags[] = { SL_BOOLEAN_TRUE }; check ((*engine.engineInterface)->CreateAudioPlayer (engine.engineInterface, &playerObject, &audioSrc, &audioSink, 1, interfaceIDs, flags)); check ((*playerObject)->Realize (playerObject, SL_BOOLEAN_FALSE)); check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_PLAY, &playerPlay)); check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &playerBufferQueue)); check ((*playerBufferQueue)->RegisterCallback (playerBufferQueue, staticCallback, this)); } ~Player() { if (playerPlay != nullptr) check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_STOPPED)); if (playerBufferQueue != nullptr) check ((*playerBufferQueue)->Clear (playerBufferQueue)); if (playerObject != nullptr) (*playerObject)->Destroy (playerObject); } bool openedOk() const noexcept { return playerBufferQueue != nullptr; } void start() { jassert (openedOk()); check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_PLAYING)); } void writeBuffer (const AudioSampleBuffer& buffer, Thread& thread) { jassert (buffer.getNumChannels() == bufferList.numChannels); jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers); int offset = 0; int numSamples = buffer.getNumSamples(); while (numSamples > 0) { int16* const destBuffer = bufferList.waitForFreeBuffer (thread); if (destBuffer == nullptr) break; for (int i = 0; i < bufferList.numChannels; ++i) { typedef AudioData::Pointer DstSampleType; typedef AudioData::Pointer SrcSampleType; DstSampleType dstData (destBuffer + i, bufferList.numChannels); SrcSampleType srcData (buffer.getReadPointer (i, offset)); dstData.convertSamples (srcData, bufferList.numSamples); } check ((*playerBufferQueue)->Enqueue (playerBufferQueue, destBuffer, bufferList.getBufferSizeBytes())); bufferList.bufferSent(); numSamples -= bufferList.numSamples; offset += bufferList.numSamples; } } private: SLObjectItf playerObject; SLPlayItf playerPlay; SLAndroidSimpleBufferQueueItf playerBufferQueue; BufferList bufferList; static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) { jassert (queue == static_cast (context)->playerBufferQueue); (void) queue; static_cast (context)->bufferList.bufferReturned(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player) }; //================================================================================================== struct Recorder { Recorder (int numChannels, int sampleRate, Engine& engine) : recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr), configObject (nullptr), bufferList (numChannels) { jassert (numChannels == 1); // STEREO doesn't always work!! SLDataFormat_PCM pcmFormat = { SL_DATAFORMAT_PCM, (SLuint32) numChannels, (SLuint32) (sampleRate * 1000), // (sample rate units are millihertz) SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, (numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT), SL_BYTEORDER_LITTLEENDIAN }; SLDataLocator_IODevice ioDevice = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr }; SLDataSource audioSrc = { &ioDevice, nullptr }; SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers }; SLDataSink audioSink = { &bufferQueue, &pcmFormat }; const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; const SLboolean flags[] = { SL_BOOLEAN_TRUE }; if (check ((*engine.engineInterface)->CreateAudioRecorder (engine.engineInterface, &recorderObject, &audioSrc, &audioSink, 1, interfaceIDs, flags))) { if (check ((*recorderObject)->Realize (recorderObject, SL_BOOLEAN_FALSE))) { check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord)); check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue)); check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDCONFIGURATION, &configObject)); check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this)); check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED)); for (int i = bufferList.numBuffers; --i >= 0;) { int16* const buffer = bufferList.getNextBuffer(); jassert (buffer != nullptr); enqueueBuffer (buffer); } } } } ~Recorder() { if (recorderRecord != nullptr) check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED)); if (recorderBufferQueue != nullptr) check ((*recorderBufferQueue)->Clear (recorderBufferQueue)); if (recorderObject != nullptr) (*recorderObject)->Destroy (recorderObject); } bool openedOk() const noexcept { return recorderBufferQueue != nullptr; } void start() { jassert (openedOk()); check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_RECORDING)); } void readNextBlock (AudioSampleBuffer& buffer, Thread& thread) { jassert (buffer.getNumChannels() == bufferList.numChannels); jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers); jassert ((buffer.getNumSamples() % bufferList.numSamples) == 0); int offset = 0; int numSamples = buffer.getNumSamples(); while (numSamples > 0) { int16* const srcBuffer = bufferList.waitForFreeBuffer (thread); if (srcBuffer == nullptr) break; for (int i = 0; i < bufferList.numChannels; ++i) { typedef AudioData::Pointer DstSampleType; typedef AudioData::Pointer SrcSampleType; DstSampleType dstData (buffer.getWritePointer (i, offset)); SrcSampleType srcData (srcBuffer + i, bufferList.numChannels); dstData.convertSamples (srcData, bufferList.numSamples); } enqueueBuffer (srcBuffer); numSamples -= bufferList.numSamples; offset += bufferList.numSamples; } } bool setAudioPreprocessingEnabled (bool enable) { SLuint32 mode = enable ? SL_ANDROID_RECORDING_PRESET_GENERIC : SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; return configObject != nullptr && check ((*configObject)->SetConfiguration (configObject, SL_ANDROID_KEY_RECORDING_PRESET, &mode, sizeof (mode))); } private: SLObjectItf recorderObject; SLRecordItf recorderRecord; SLAndroidSimpleBufferQueueItf recorderBufferQueue; SLAndroidConfigurationItf configObject; BufferList bufferList; void enqueueBuffer (int16* buffer) { check ((*recorderBufferQueue)->Enqueue (recorderBufferQueue, buffer, bufferList.getBufferSizeBytes())); bufferList.bufferSent(); } static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) { jassert (queue == static_cast (context)->recorderBufferQueue); (void) queue; static_cast (context)->bufferList.bufferReturned(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Recorder) }; //============================================================================== Engine engine; ScopedPointer player; ScopedPointer recorder; //============================================================================== static bool check (const SLresult result) { jassert (result == SL_RESULT_SUCCESS); return result == SL_RESULT_SUCCESS; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice) }; //============================================================================== class OpenSLAudioDeviceType : public AudioIODeviceType { public: OpenSLAudioDeviceType() : AudioIODeviceType (openSLTypeName) {} //============================================================================== void scanForDevices() {} StringArray getDeviceNames (bool wantInputNames) const { return StringArray (openSLTypeName); } int getDefaultDeviceIndex (bool forInput) const { return 0; } int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; } bool hasSeparateInputsAndOutputs() const { return false; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { ScopedPointer dev; if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) { dev = new OpenSLAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName); if (! dev->openedOk()) dev = nullptr; } return dev.release(); } private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioDeviceType) }; //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp000066400000000000000000000520631320201440200323360ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class iOSAudioIODevice : public AudioIODevice { public: iOSAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, "Audio"), actualBufferSize (0), isRunning (false), audioUnit (0), callback (nullptr), floatData (1, 2) { getSessionHolder().activeDevices.add (this); numInputChannels = 2; numOutputChannels = 2; preferredBufferSize = 0; updateDeviceInfo(); } ~iOSAudioIODevice() { getSessionHolder().activeDevices.removeFirstMatchingValue (this); close(); } StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); s.add ("Right"); return s; } StringArray getInputChannelNames() override { StringArray s; if (audioInputIsAvailable) { s.add ("Left"); s.add ("Right"); } return s; } Array getAvailableSampleRates() override { // can't find a good way to actually ask the device for which of these it supports.. static const double rates[] = { 8000.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0 }; return Array (rates, numElementsInArray (rates)); } Array getAvailableBufferSizes() override { Array r; for (int i = 6; i < 12; ++i) r.add (1 << i); return r; } int getDefaultBufferSize() override { return 1024; } String open (const BigInteger& inputChannelsWanted, const BigInteger& outputChannelsWanted, double targetSampleRate, int bufferSize) override { close(); lastError.clear(); preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; // xxx set up channel mapping activeOutputChans = outputChannelsWanted; activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); numOutputChannels = activeOutputChans.countNumberOfSetBits(); monoOutputChannelNumber = activeOutputChans.findNextSetBit (0); activeInputChans = inputChannelsWanted; activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); numInputChannels = activeInputChans.countNumberOfSetBits(); monoInputChannelNumber = activeInputChans.findNextSetBit (0); AudioSessionSetActive (true); if (numInputChannels > 0 && audioInputIsAvailable) { setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_PlayAndRecord); setSessionUInt32Property (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, 1); } else { setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback); } AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); fixAudioRouteIfSetToReceiver(); setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate); updateDeviceInfo(); setSessionFloat32Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate); updateCurrentBufferSize(); prepareFloatBuffers (actualBufferSize); isRunning = true; routingChanged (nullptr); // creates and starts the AU lastError = audioUnit != 0 ? "" : "Couldn't open the device"; return lastError; } void close() override { if (isRunning) { isRunning = false; setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback); AudioSessionRemovePropertyListenerWithUserData (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); AudioSessionSetActive (false); if (audioUnit != 0) { AudioComponentInstanceDispose (audioUnit); audioUnit = 0; } } } bool isOpen() override { return isRunning; } int getCurrentBufferSizeSamples() override { return actualBufferSize; } double getCurrentSampleRate() override { return sampleRate; } int getCurrentBitDepth() override { return 16; } BigInteger getActiveOutputChannels() const override { return activeOutputChans; } BigInteger getActiveInputChannels() const override { return activeInputChans; } int getOutputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareOutputLatency); } int getInputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareInputLatency); } int getLatency (AudioSessionPropertyID propID) { Float32 latency = 0; getSessionProperty (propID, latency); return roundToInt (latency * getCurrentSampleRate()); } void start (AudioIODeviceCallback* newCallback) override { if (isRunning && callback != newCallback) { if (newCallback != nullptr) newCallback->audioDeviceAboutToStart (this); const ScopedLock sl (callbackLock); callback = newCallback; } } void stop() override { if (isRunning) { AudioIODeviceCallback* lastCallback; { const ScopedLock sl (callbackLock); lastCallback = callback; callback = nullptr; } if (lastCallback != nullptr) lastCallback->audioDeviceStopped(); } } bool isPlaying() override { return isRunning && callback != nullptr; } String getLastError() override { return lastError; } bool setAudioPreprocessingEnabled (bool enable) override { return setSessionUInt32Property (kAudioSessionProperty_Mode, enable ? kAudioSessionMode_Default : kAudioSessionMode_Measurement); } private: //================================================================================================== CriticalSection callbackLock; Float64 sampleRate; int numInputChannels, numOutputChannels; int preferredBufferSize, actualBufferSize; bool isRunning; String lastError; AudioStreamBasicDescription format; AudioUnit audioUnit; UInt32 audioInputIsAvailable; AudioIODeviceCallback* callback; BigInteger activeOutputChans, activeInputChans; AudioSampleBuffer floatData; float* inputChannels[3]; float* outputChannels[3]; bool monoInputChannelNumber, monoOutputChannelNumber; void prepareFloatBuffers (int bufferSize) { if (numInputChannels + numOutputChannels > 0) { floatData.setSize (numInputChannels + numOutputChannels, bufferSize); zeromem (inputChannels, sizeof (inputChannels)); zeromem (outputChannels, sizeof (outputChannels)); for (int i = 0; i < numInputChannels; ++i) inputChannels[i] = floatData.getWritePointer (i); for (int i = 0; i < numOutputChannels; ++i) outputChannels[i] = floatData.getWritePointer (i + numInputChannels); } } //================================================================================================== OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, const UInt32 numFrames, AudioBufferList* data) { OSStatus err = noErr; if (audioInputIsAvailable && numInputChannels > 0) err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data); const ScopedLock sl (callbackLock); if (callback != nullptr) { if ((int) numFrames > floatData.getNumSamples()) prepareFloatBuffers ((int) numFrames); if (audioInputIsAvailable && numInputChannels > 0) { short* shortData = (short*) data->mBuffers[0].mData; if (numInputChannels >= 2) { for (UInt32 i = 0; i < numFrames; ++i) { inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); inputChannels[1][i] = *shortData++ * (1.0f / 32768.0f); } } else { if (monoInputChannelNumber > 0) ++shortData; for (UInt32 i = 0; i < numFrames; ++i) { inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); ++shortData; } } } else { for (int i = numInputChannels; --i >= 0;) zeromem (inputChannels[i], sizeof (float) * numFrames); } callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels, outputChannels, numOutputChannels, (int) numFrames); short* shortData = (short*) data->mBuffers[0].mData; int n = 0; if (numOutputChannels >= 2) { for (UInt32 i = 0; i < numFrames; ++i) { shortData [n++] = (short) (outputChannels[0][i] * 32767.0f); shortData [n++] = (short) (outputChannels[1][i] * 32767.0f); } } else if (numOutputChannels == 1) { for (UInt32 i = 0; i < numFrames; ++i) { const short s = (short) (outputChannels[monoOutputChannelNumber][i] * 32767.0f); shortData [n++] = s; shortData [n++] = s; } } else { zeromem (data->mBuffers[0].mData, 2 * sizeof (short) * numFrames); } } else { zeromem (data->mBuffers[0].mData, 2 * sizeof (short) * numFrames); } return err; } void updateDeviceInfo() { getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate); getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable); } void updateCurrentBufferSize() { Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f; getSessionProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, bufferDuration); actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); } void routingChanged (const void* propertyValue) { if (! isRunning) return; if (propertyValue != nullptr) { CFDictionaryRef routeChangeDictionary = (CFDictionaryRef) propertyValue; CFNumberRef routeChangeReasonRef = (CFNumberRef) CFDictionaryGetValue (routeChangeDictionary, CFSTR (kAudioSession_AudioRouteChangeKey_Reason)); SInt32 routeChangeReason; CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) { const ScopedLock sl (callbackLock); if (callback != nullptr) callback->audioDeviceError ("Old device unavailable"); } } updateDeviceInfo(); createAudioUnit(); AudioSessionSetActive (true); if (audioUnit != 0) { UInt32 formatSize = sizeof (format); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize); updateCurrentBufferSize(); AudioOutputUnitStart (audioUnit); } } //================================================================================================== struct AudioSessionHolder { AudioSessionHolder() { AudioSessionInitialize (0, 0, interruptionListenerCallback, this); } static void interruptionListenerCallback (void* client, UInt32 interruptionType) { const Array & activeDevices = static_cast (client)->activeDevices; for (int i = activeDevices.size(); --i >= 0;) activeDevices.getUnchecked(i)->interruptionListener (interruptionType); } Array activeDevices; }; static AudioSessionHolder& getSessionHolder() { static AudioSessionHolder audioSessionHolder; return audioSessionHolder; } void interruptionListener (const UInt32 interruptionType) { if (interruptionType == kAudioSessionBeginInterruption) { isRunning = false; AudioOutputUnitStop (audioUnit); AudioSessionSetActive (false); const ScopedLock sl (callbackLock); if (callback != nullptr) callback->audioDeviceError ("iOS audio session interruption"); } if (interruptionType == kAudioSessionEndInterruption) { isRunning = true; AudioSessionSetActive (true); AudioOutputUnitStart (audioUnit); const ScopedLock sl (callbackLock); if (callback != nullptr) callback->audioDeviceError ("iOS audio session resumed"); } } //================================================================================================== static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) { return static_cast (client)->process (flags, time, numFrames, data); } static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue) { static_cast (client)->routingChanged (propertyValue); } //================================================================================================== void resetFormat (const int numChannels) noexcept { zerostruct (format); format.mFormatID = kAudioFormatLinearPCM; format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kAudioFormatFlagsNativeEndian; format.mBitsPerChannel = 8 * sizeof (short); format.mChannelsPerFrame = (UInt32) numChannels; format.mFramesPerPacket = 1; format.mBytesPerFrame = format.mBytesPerPacket = (UInt32) numChannels * sizeof (short); } bool createAudioUnit() { if (audioUnit != 0) { AudioComponentInstanceDispose (audioUnit); audioUnit = 0; } resetFormat (2); AudioComponentDescription desc; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_RemoteIO; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; AudioComponent comp = AudioComponentFindNext (0, &desc); AudioComponentInstanceNew (comp, &audioUnit); if (audioUnit == 0) return false; if (numInputChannels > 0) { const UInt32 one = 1; AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one)); } { AudioChannelLayout layout; layout.mChannelBitmap = 0; layout.mNumberChannelDescriptions = 0; layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout)); AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout)); } { AURenderCallbackStruct inputProc; inputProc.inputProc = processStatic; inputProc.inputProcRefCon = this; AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputProc, sizeof (inputProc)); } AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format)); AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format)); AudioUnitInitialize (audioUnit); return true; } // If the routing is set to go through the receiver (i.e. the speaker, but quiet), this re-routes it // to make it loud. Needed because by default when using an input + output, the output is kept quiet. static void fixAudioRouteIfSetToReceiver() { CFStringRef audioRoute = 0; if (getSessionProperty (kAudioSessionProperty_AudioRoute, audioRoute) == noErr) { NSString* route = (NSString*) audioRoute; //DBG ("audio route: " + nsStringToJuce (route)); if ([route hasPrefix: @"Receiver"]) setSessionUInt32Property (kAudioSessionProperty_OverrideAudioRoute, kAudioSessionOverrideAudioRoute_Speaker); CFRelease (audioRoute); } } template static OSStatus getSessionProperty (AudioSessionPropertyID propID, Type& result) noexcept { UInt32 valueSize = sizeof (result); return AudioSessionGetProperty (propID, &valueSize, &result); } static bool setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } static bool setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } static bool setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) }; //============================================================================== class iOSAudioIODeviceType : public AudioIODeviceType { public: iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") {} void scanForDevices() {} StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray ("iOS Audio"); } int getDefaultDeviceIndex (bool /*forInput*/) const { return 0; } int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; } bool hasSeparateInputsAndOutputs() const { return false; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) return new iOSAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName); return nullptr; } private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSAudioIODeviceType) }; //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return new iOSAudioIODeviceType(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp000066400000000000000000001313461320201440200323640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace { #ifndef JUCE_ALSA_LOGGING #define JUCE_ALSA_LOGGING 0 #endif #if JUCE_ALSA_LOGGING #define JUCE_ALSA_LOG(dbgtext) { juce::String tempDbgBuf ("ALSA: "); tempDbgBuf << dbgtext; Logger::writeToLog (tempDbgBuf); DBG (tempDbgBuf); } #define JUCE_CHECKED_RESULT(x) (logErrorMessage (x, __LINE__)) static int logErrorMessage (int err, int lineNum) { if (err < 0) JUCE_ALSA_LOG ("Error: line " << lineNum << ": code " << err << " (" << snd_strerror (err) << ")"); return err; } #else #define JUCE_ALSA_LOG(x) {} #define JUCE_CHECKED_RESULT(x) (x) #endif #define JUCE_ALSA_FAILED(x) failed (x) static void getDeviceSampleRates (snd_pcm_t* handle, Array& rates) { const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; snd_pcm_hw_params_t* hwParams; snd_pcm_hw_params_alloca (&hwParams); for (int i = 0; ratesToTry[i] != 0; ++i) { if (snd_pcm_hw_params_any (handle, hwParams) >= 0 && snd_pcm_hw_params_test_rate (handle, hwParams, (unsigned int) ratesToTry[i], 0) == 0) { rates.addIfNotAlreadyThere ((double) ratesToTry[i]); } } } static void getDeviceNumChannels (snd_pcm_t* handle, unsigned int* minChans, unsigned int* maxChans) { snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca (¶ms); if (snd_pcm_hw_params_any (handle, params) >= 0) { snd_pcm_hw_params_get_channels_min (params, minChans); snd_pcm_hw_params_get_channels_max (params, maxChans); JUCE_ALSA_LOG ("getDeviceNumChannels: " << (int) *minChans << " " << (int) *maxChans); // some virtual devices (dmix for example) report 10000 channels , we have to clamp these values *maxChans = jmin (*maxChans, 32u); *minChans = jmin (*minChans, *maxChans); } else { JUCE_ALSA_LOG ("getDeviceNumChannels failed"); } } static void getDeviceProperties (const String& deviceID, unsigned int& minChansOut, unsigned int& maxChansOut, unsigned int& minChansIn, unsigned int& maxChansIn, Array& rates, bool testOutput, bool testInput) { minChansOut = maxChansOut = minChansIn = maxChansIn = 0; if (deviceID.isEmpty()) return; JUCE_ALSA_LOG ("getDeviceProperties(" << deviceID.toUTF8().getAddress() << ")"); snd_pcm_info_t* info; snd_pcm_info_alloca (&info); if (testOutput) { snd_pcm_t* pcmHandle; if (JUCE_CHECKED_RESULT (snd_pcm_open (&pcmHandle, deviceID.toUTF8().getAddress(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) >= 0) { getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut); getDeviceSampleRates (pcmHandle, rates); snd_pcm_close (pcmHandle); } } if (testInput) { snd_pcm_t* pcmHandle; if (JUCE_CHECKED_RESULT (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) >= 0)) { getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn); if (rates.size() == 0) getDeviceSampleRates (pcmHandle, rates); snd_pcm_close (pcmHandle); } } } static void ensureMinimumNumBitsSet (BigInteger& chans, int minNumChans) { int i = 0; while (chans.countNumberOfSetBits() < minNumChans) chans.setBit (i++); } static void silentErrorHandler (const char*, int, const char*, int, const char*,...) {} //============================================================================== class ALSADevice { public: ALSADevice (const String& devID, bool forInput) : handle (nullptr), bitDepth (16), numChannelsRunning (0), latency (0), deviceID (devID), isInput (forInput), isInterleaved (true) { JUCE_ALSA_LOG ("snd_pcm_open (" << deviceID.toUTF8().getAddress() << ", forInput=" << forInput << ")"); int err = snd_pcm_open (&handle, deviceID.toUTF8(), forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC); if (err < 0) { if (-err == EBUSY) error << "The device \"" << deviceID << "\" is busy (another application is using it)."; else if (-err == ENOENT) error << "The device \"" << deviceID << "\" is not available."; else error << "Could not open " << (forInput ? "input" : "output") << " device \"" << deviceID << "\": " << snd_strerror(err) << " (" << err << ")"; JUCE_ALSA_LOG ("snd_pcm_open failed; " << error); } } ~ALSADevice() { closeNow(); } void closeNow() { if (handle != nullptr) { snd_pcm_close (handle); handle = nullptr; } } bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize) { if (handle == nullptr) return false; JUCE_ALSA_LOG ("ALSADevice::setParameters(" << deviceID << ", " << (int) sampleRate << ", " << numChannels << ", " << bufferSize << ")"); snd_pcm_hw_params_t* hwParams; snd_pcm_hw_params_alloca (&hwParams); if (snd_pcm_hw_params_any (handle, hwParams) < 0) { // this is the error message that aplay returns when an error happens here, // it is a bit more explicit that "Invalid parameter" error = "Broken configuration for this PCM: no configurations available"; return false; } if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0) // works better for plughw.. isInterleaved = true; else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0) isInterleaved = false; else { jassertfalse; return false; } enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17, onlyUseLower24Bits = 1 << 18 }; const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32 | isFloatBit | isLittleEndianBit, SND_PCM_FORMAT_FLOAT_BE, 32 | isFloatBit, SND_PCM_FORMAT_S32_LE, 32 | isLittleEndianBit, SND_PCM_FORMAT_S32_BE, 32, SND_PCM_FORMAT_S24_3LE, 24 | isLittleEndianBit, SND_PCM_FORMAT_S24_3BE, 24, SND_PCM_FORMAT_S24_LE, 32 | isLittleEndianBit | onlyUseLower24Bits, SND_PCM_FORMAT_S16_LE, 16 | isLittleEndianBit, SND_PCM_FORMAT_S16_BE, 16 }; bitDepth = 0; for (int i = 0; i < numElementsInArray (formatsToTry); i += 2) { if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0) { const int type = formatsToTry [i + 1]; bitDepth = type & 255; converter = createConverter (isInput, bitDepth, (type & isFloatBit) != 0, (type & isLittleEndianBit) != 0, (type & onlyUseLower24Bits) != 0, numChannels); break; } } if (bitDepth == 0) { error = "device doesn't support a compatible PCM format"; JUCE_ALSA_LOG ("Error: " + error); return false; } int dir = 0; unsigned int periods = 4; snd_pcm_uframes_t samplesPerPeriod = (snd_pcm_uframes_t) bufferSize; if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, (unsigned int ) numChannels)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params (handle, hwParams))) { return false; } snd_pcm_uframes_t frames = 0; if (JUCE_ALSA_FAILED (snd_pcm_hw_params_get_period_size (hwParams, &frames, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) latency = 0; else latency = (int) frames * ((int) periods - 1); // (this is the method JACK uses to guess the latency..) JUCE_ALSA_LOG ("frames: " << (int) frames << ", periods: " << (int) periods << ", samplesPerPeriod: " << (int) samplesPerPeriod); snd_pcm_sw_params_t* swParams; snd_pcm_sw_params_alloca (&swParams); snd_pcm_uframes_t boundary; if (JUCE_ALSA_FAILED (snd_pcm_sw_params_current (handle, swParams)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_get_boundary (swParams, &boundary)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_size (handle, swParams, boundary)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod)) || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_stop_threshold (handle, swParams, boundary)) || JUCE_ALSA_FAILED (snd_pcm_sw_params (handle, swParams))) { return false; } #if JUCE_ALSA_LOGGING // enable this to dump the config of the devices that get opened snd_output_t* out; snd_output_stdio_attach (&out, stderr, 0); snd_pcm_hw_params_dump (hwParams, out); snd_pcm_sw_params_dump (swParams, out); #endif numChannelsRunning = numChannels; return true; } //============================================================================== bool writeToOutputDevice (AudioSampleBuffer& outputChannelBuffer, const int numSamples) { jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels()); float* const* const data = outputChannelBuffer.getArrayOfWritePointers(); snd_pcm_sframes_t numDone = 0; if (isInterleaved) { scratch.ensureSize ((size_t) ((int) sizeof (float) * numSamples * numChannelsRunning), false); for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples); numDone = snd_pcm_writei (handle, scratch.getData(), (snd_pcm_uframes_t) numSamples); } else { for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (data[i], data[i], numSamples); numDone = snd_pcm_writen (handle, (void**) data, (snd_pcm_uframes_t) numSamples); } if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) numDone, 1 /* silent */))) return false; if (numDone < numSamples) JUCE_ALSA_LOG ("Did not write all samples: numDone: " << numDone << ", numSamples: " << numSamples); return true; } bool readFromInputDevice (AudioSampleBuffer& inputChannelBuffer, const int numSamples) { jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels()); float* const* const data = inputChannelBuffer.getArrayOfWritePointers(); if (isInterleaved) { scratch.ensureSize ((size_t) ((int) sizeof (float) * numSamples * numChannelsRunning), false); scratch.fillWith (0); // (not clearing this data causes warnings in valgrind) snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), (snd_pcm_uframes_t) numSamples); if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) return false; if (num < numSamples) JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples); for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (data[i], 0, scratch.getData(), i, numSamples); } else { snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, (snd_pcm_uframes_t) numSamples); if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) return false; if (num < numSamples) JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples); for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (data[i], data[i], numSamples); } return true; } //============================================================================== snd_pcm_t* handle; String error; int bitDepth, numChannelsRunning, latency; private: //============================================================================== String deviceID; const bool isInput; bool isInterleaved; MemoryBlock scratch; ScopedPointer converter; //============================================================================== template struct ConverterHelper { static AudioData::Converter* createConverter (const bool forInput, const bool isLittleEndian, const int numInterleavedChannels) { if (forInput) { typedef AudioData::Pointer DestType; if (isLittleEndian) return new AudioData::ConverterInstance , DestType> (numInterleavedChannels, 1); return new AudioData::ConverterInstance , DestType> (numInterleavedChannels, 1); } typedef AudioData::Pointer SourceType; if (isLittleEndian) return new AudioData::ConverterInstance > (1, numInterleavedChannels); return new AudioData::ConverterInstance > (1, numInterleavedChannels); } }; static AudioData::Converter* createConverter (bool forInput, int bitDepth, bool isFloat, bool isLittleEndian, bool useOnlyLower24Bits, int numInterleavedChannels) { JUCE_ALSA_LOG ("format: bitDepth=" << bitDepth << ", isFloat=" << isFloat << ", isLittleEndian=" << isLittleEndian << ", numChannels=" << numInterleavedChannels); if (isFloat) return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); if (bitDepth == 16) return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); if (bitDepth == 24) return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); jassert (bitDepth == 32); if (useOnlyLower24Bits) return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); } //============================================================================== bool failed (const int errorNum) { if (errorNum >= 0) return false; error = snd_strerror (errorNum); JUCE_ALSA_LOG ("ALSA error: " << error); return true; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSADevice) }; //============================================================================== class ALSAThread : public Thread { public: ALSAThread (const String& inputDeviceID, const String& outputDeviceID) : Thread ("Juce ALSA"), sampleRate (0), bufferSize (0), outputLatency (0), inputLatency (0), callback (0), inputId (inputDeviceID), outputId (outputDeviceID), numCallbacks (0), audioIoInProgress (false), inputChannelBuffer (1, 1), outputChannelBuffer (1, 1) { initialiseRatesAndChannels(); } ~ALSAThread() { close(); } void open (BigInteger inputChannels, BigInteger outputChannels, const double newSampleRate, const int newBufferSize) { close(); error.clear(); sampleRate = newSampleRate; bufferSize = newBufferSize; int maxInputsRequested = inputChannels.getHighestBit() + 1; maxInputsRequested = jmax ((int) minChansIn, jmin ((int) maxChansIn, maxInputsRequested)); inputChannelBuffer.setSize (maxInputsRequested, bufferSize); inputChannelBuffer.clear(); inputChannelDataForCallback.clear(); currentInputChans.clear(); if (inputChannels.getHighestBit() >= 0) { for (int i = 0; i < maxInputsRequested; ++i) { if (inputChannels[i]) { inputChannelDataForCallback.add (inputChannelBuffer.getReadPointer (i)); currentInputChans.setBit (i); } } } ensureMinimumNumBitsSet (outputChannels, (int) minChansOut); int maxOutputsRequested = outputChannels.getHighestBit() + 1; maxOutputsRequested = jmax ((int) minChansOut, jmin ((int) maxChansOut, maxOutputsRequested)); outputChannelBuffer.setSize (maxOutputsRequested, bufferSize); outputChannelBuffer.clear(); outputChannelDataForCallback.clear(); currentOutputChans.clear(); if (outputChannels.getHighestBit() >= 0) { for (int i = 0; i < maxOutputsRequested; ++i) { if (outputChannels[i]) { outputChannelDataForCallback.add (outputChannelBuffer.getWritePointer (i)); currentOutputChans.setBit (i); } } } if (outputChannelDataForCallback.size() > 0 && outputId.isNotEmpty()) { outputDevice = new ALSADevice (outputId, false); if (outputDevice->error.isNotEmpty()) { error = outputDevice->error; outputDevice = nullptr; return; } if (! outputDevice->setParameters ((unsigned int) sampleRate, jlimit ((int) minChansOut, (int) maxChansOut, currentOutputChans.getHighestBit() + 1), bufferSize)) { error = outputDevice->error; outputDevice = nullptr; return; } outputLatency = outputDevice->latency; } if (inputChannelDataForCallback.size() > 0 && inputId.isNotEmpty()) { inputDevice = new ALSADevice (inputId, true); if (inputDevice->error.isNotEmpty()) { error = inputDevice->error; inputDevice = nullptr; return; } ensureMinimumNumBitsSet (currentInputChans, (int) minChansIn); if (! inputDevice->setParameters ((unsigned int) sampleRate, jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1), bufferSize)) { error = inputDevice->error; inputDevice = nullptr; return; } inputLatency = inputDevice->latency; } if (outputDevice == nullptr && inputDevice == nullptr) { error = "no channels"; return; } if (outputDevice != nullptr && inputDevice != nullptr) snd_pcm_link (outputDevice->handle, inputDevice->handle); if (inputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (inputDevice->handle))) return; if (outputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (outputDevice->handle))) return; startThread (9); int count = 1000; while (numCallbacks == 0) { sleep (5); if (--count < 0 || ! isThreadRunning()) { error = "device didn't start"; break; } } } void close() { if (isThreadRunning()) { // problem: when pulseaudio is suspended (with pasuspend) , the ALSAThread::run is just stuck in // snd_pcm_writei -- no error, no nothing it just stays stuck. So the only way I found to exit "nicely" // (that is without the "killing thread by force" of stopThread) , is to just call snd_pcm_close from // here which will cause the thread to resume, and exit signalThreadShouldExit(); const int callbacksToStop = numCallbacks; if ((! waitForThreadToExit (400)) && audioIoInProgress && numCallbacks == callbacksToStop) { JUCE_ALSA_LOG ("Thread is stuck in i/o.. Is pulseaudio suspended?"); if (outputDevice != nullptr) outputDevice->closeNow(); if (inputDevice != nullptr) inputDevice->closeNow(); } } stopThread (6000); inputDevice = nullptr; outputDevice = nullptr; inputChannelBuffer.setSize (1, 1); outputChannelBuffer.setSize (1, 1); numCallbacks = 0; } void setCallback (AudioIODeviceCallback* const newCallback) noexcept { const ScopedLock sl (callbackLock); callback = newCallback; } void run() override { while (! threadShouldExit()) { if (inputDevice != nullptr && inputDevice->handle != nullptr) { if (outputDevice == nullptr || outputDevice->handle == nullptr) { JUCE_ALSA_FAILED (snd_pcm_wait (inputDevice->handle, 2000)); if (threadShouldExit()) break; snd_pcm_sframes_t avail = snd_pcm_avail_update (inputDevice->handle); if (avail < 0) JUCE_ALSA_FAILED (snd_pcm_recover (inputDevice->handle, (int) avail, 0)); } audioIoInProgress = true; if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize)) { JUCE_ALSA_LOG ("Read failure"); break; } audioIoInProgress = false; } if (threadShouldExit()) break; { const ScopedLock sl (callbackLock); ++numCallbacks; if (callback != nullptr) { callback->audioDeviceIOCallback (inputChannelDataForCallback.getRawDataPointer(), inputChannelDataForCallback.size(), outputChannelDataForCallback.getRawDataPointer(), outputChannelDataForCallback.size(), bufferSize); } else { for (int i = 0; i < outputChannelDataForCallback.size(); ++i) zeromem (outputChannelDataForCallback[i], sizeof (float) * (size_t) bufferSize); } } if (outputDevice != nullptr && outputDevice->handle != nullptr) { JUCE_ALSA_FAILED (snd_pcm_wait (outputDevice->handle, 2000)); if (threadShouldExit()) break; snd_pcm_sframes_t avail = snd_pcm_avail_update (outputDevice->handle); if (avail < 0) JUCE_ALSA_FAILED (snd_pcm_recover (outputDevice->handle, (int) avail, 0)); audioIoInProgress = true; if (! outputDevice->writeToOutputDevice (outputChannelBuffer, bufferSize)) { JUCE_ALSA_LOG ("write failure"); break; } audioIoInProgress = false; } } audioIoInProgress = false; } int getBitDepth() const noexcept { if (outputDevice != nullptr) return outputDevice->bitDepth; if (inputDevice != nullptr) return inputDevice->bitDepth; return 16; } //============================================================================== String error; double sampleRate; int bufferSize, outputLatency, inputLatency; BigInteger currentInputChans, currentOutputChans; Array sampleRates; StringArray channelNamesOut, channelNamesIn; AudioIODeviceCallback* callback; private: //============================================================================== const String inputId, outputId; ScopedPointer outputDevice, inputDevice; int numCallbacks; bool audioIoInProgress; CriticalSection callbackLock; AudioSampleBuffer inputChannelBuffer, outputChannelBuffer; Array inputChannelDataForCallback; Array outputChannelDataForCallback; unsigned int minChansOut, maxChansOut; unsigned int minChansIn, maxChansIn; bool failed (const int errorNum) { if (errorNum >= 0) return false; error = snd_strerror (errorNum); JUCE_ALSA_LOG ("ALSA error: " << error); return true; } void initialiseRatesAndChannels() { sampleRates.clear(); channelNamesOut.clear(); channelNamesIn.clear(); minChansOut = 0; maxChansOut = 0; minChansIn = 0; maxChansIn = 0; unsigned int dummy = 0; getDeviceProperties (inputId, dummy, dummy, minChansIn, maxChansIn, sampleRates, false, true); getDeviceProperties (outputId, minChansOut, maxChansOut, dummy, dummy, sampleRates, true, false); for (unsigned int i = 0; i < maxChansOut; ++i) channelNamesOut.add ("channel " + String ((int) i + 1)); for (unsigned int i = 0; i < maxChansIn; ++i) channelNamesIn.add ("channel " + String ((int) i + 1)); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAThread) }; //============================================================================== class ALSAAudioIODevice : public AudioIODevice { public: ALSAAudioIODevice (const String& deviceName, const String& deviceTypeName, const String& inputDeviceID, const String& outputDeviceID) : AudioIODevice (deviceName, deviceTypeName), inputId (inputDeviceID), outputId (outputDeviceID), isOpen_ (false), isStarted (false), internal (inputDeviceID, outputDeviceID) { } ~ALSAAudioIODevice() { close(); } StringArray getOutputChannelNames() override { return internal.channelNamesOut; } StringArray getInputChannelNames() override { return internal.channelNamesIn; } Array getAvailableSampleRates() override { return internal.sampleRates; } Array getAvailableBufferSizes() override { Array r; int n = 16; for (int i = 0; i < 50; ++i) { r.add (n); n += n < 64 ? 16 : (n < 512 ? 32 : (n < 1024 ? 64 : (n < 2048 ? 128 : 256))); } return r; } int getDefaultBufferSize() override { return 512; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) override { close(); if (bufferSizeSamples <= 0) bufferSizeSamples = getDefaultBufferSize(); if (sampleRate <= 0) { for (int i = 0; i < internal.sampleRates.size(); ++i) { double rate = internal.sampleRates[i]; if (rate >= 44100) { sampleRate = rate; break; } } } internal.open (inputChannels, outputChannels, sampleRate, bufferSizeSamples); isOpen_ = internal.error.isEmpty(); return internal.error; } void close() override { stop(); internal.close(); isOpen_ = false; } bool isOpen() override { return isOpen_; } bool isPlaying() override { return isStarted && internal.error.isEmpty(); } String getLastError() override { return internal.error; } int getCurrentBufferSizeSamples() override { return internal.bufferSize; } double getCurrentSampleRate() override { return internal.sampleRate; } int getCurrentBitDepth() override { return internal.getBitDepth(); } BigInteger getActiveOutputChannels() const override { return internal.currentOutputChans; } BigInteger getActiveInputChannels() const override { return internal.currentInputChans; } int getOutputLatencyInSamples() override { return internal.outputLatency; } int getInputLatencyInSamples() override { return internal.inputLatency; } void start (AudioIODeviceCallback* callback) override { if (! isOpen_) callback = nullptr; if (callback != nullptr) callback->audioDeviceAboutToStart (this); internal.setCallback (callback); isStarted = (callback != nullptr); } void stop() override { AudioIODeviceCallback* const oldCallback = internal.callback; start (nullptr); if (oldCallback != nullptr) oldCallback->audioDeviceStopped(); } String inputId, outputId; private: bool isOpen_, isStarted; ALSAThread internal; }; //============================================================================== class ALSAAudioIODeviceType : public AudioIODeviceType { public: ALSAAudioIODeviceType (bool onlySoundcards, const String &deviceTypeName) : AudioIODeviceType (deviceTypeName), hasScanned (false), listOnlySoundcards (onlySoundcards) { #if ! JUCE_ALSA_LOGGING snd_lib_error_set_handler (&silentErrorHandler); #endif } ~ALSAAudioIODeviceType() { #if ! JUCE_ALSA_LOGGING snd_lib_error_set_handler (nullptr); #endif snd_config_update_free_global(); // prevent valgrind from screaming about alsa leaks } //============================================================================== void scanForDevices() { if (hasScanned) return; hasScanned = true; inputNames.clear(); inputIds.clear(); outputNames.clear(); outputIds.clear(); JUCE_ALSA_LOG ("scanForDevices()"); if (listOnlySoundcards) enumerateAlsaSoundcards(); else enumerateAlsaPCMDevices(); inputNames.appendNumbersToDuplicates (false, true); outputNames.appendNumbersToDuplicates (false, true); } StringArray getDeviceNames (bool wantInputNames) const { jassert (hasScanned); // need to call scanForDevices() before doing this return wantInputNames ? inputNames : outputNames; } int getDefaultDeviceIndex (bool forInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this const int idx = (forInput ? inputIds : outputIds).indexOf ("default"); return idx >= 0 ? idx : 0; } bool hasSeparateInputsAndOutputs() const { return true; } int getIndexOfDevice (AudioIODevice* device, bool asInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this if (ALSAAudioIODevice* d = dynamic_cast (device)) return asInput ? inputIds.indexOf (d->inputId) : outputIds.indexOf (d->outputId); return -1; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { jassert (hasScanned); // need to call scanForDevices() before doing this const int inputIndex = inputNames.indexOf (inputDeviceName); const int outputIndex = outputNames.indexOf (outputDeviceName); String deviceName (outputIndex >= 0 ? outputDeviceName : inputDeviceName); if (inputIndex >= 0 || outputIndex >= 0) return new ALSAAudioIODevice (deviceName, getTypeName(), inputIds [inputIndex], outputIds [outputIndex]); return nullptr; } private: //============================================================================== StringArray inputNames, outputNames, inputIds, outputIds; bool hasScanned, listOnlySoundcards; bool testDevice (const String &id, const String &outputName, const String &inputName) { unsigned int minChansOut = 0, maxChansOut = 0; unsigned int minChansIn = 0, maxChansIn = 0; Array rates; bool isInput = inputName.isNotEmpty(), isOutput = outputName.isNotEmpty(); getDeviceProperties (id, minChansOut, maxChansOut, minChansIn, maxChansIn, rates, isOutput, isInput); isInput = maxChansIn > 0; isOutput = maxChansOut > 0; if ((isInput || isOutput) && rates.size() > 0) { JUCE_ALSA_LOG ("testDevice: '" << id.toUTF8().getAddress() << "' -> isInput: " << isInput << ", isOutput: " << isOutput); if (isInput) { inputNames.add (inputName); inputIds.add (id); } if (isOutput) { outputNames.add (outputName); outputIds.add (id); } return isInput || isOutput; } return false; } void enumerateAlsaSoundcards() { snd_ctl_t* handle = nullptr; snd_ctl_card_info_t* info = nullptr; snd_ctl_card_info_alloca (&info); int cardNum = -1; while (outputIds.size() + inputIds.size() <= 64) { snd_card_next (&cardNum); if (cardNum < 0) break; if (JUCE_CHECKED_RESULT (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toUTF8(), SND_CTL_NONBLOCK)) >= 0) { if (JUCE_CHECKED_RESULT (snd_ctl_card_info (handle, info)) >= 0) { String cardId (snd_ctl_card_info_get_id (info)); if (cardId.removeCharacters ("0123456789").isEmpty()) cardId = String (cardNum); String cardName = snd_ctl_card_info_get_name (info); if (cardName.isEmpty()) cardName = cardId; int device = -1; snd_pcm_info_t* pcmInfo; snd_pcm_info_alloca (&pcmInfo); for (;;) { if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0) break; snd_pcm_info_set_device (pcmInfo, (unsigned int) device); for (unsigned int subDevice = 0, nbSubDevice = 1; subDevice < nbSubDevice; ++subDevice) { snd_pcm_info_set_subdevice (pcmInfo, subDevice); snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_CAPTURE); const bool isInput = (snd_ctl_pcm_info (handle, pcmInfo) >= 0); snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_PLAYBACK); const bool isOutput = (snd_ctl_pcm_info (handle, pcmInfo) >= 0); if (! (isInput || isOutput)) continue; if (nbSubDevice == 1) nbSubDevice = snd_pcm_info_get_subdevices_count (pcmInfo); String id, name; if (nbSubDevice == 1) { id << "hw:" << cardId << "," << device; name << cardName << ", " << snd_pcm_info_get_name (pcmInfo); } else { id << "hw:" << cardId << "," << device << "," << (int) subDevice; name << cardName << ", " << snd_pcm_info_get_name (pcmInfo) << " {" << snd_pcm_info_get_subdevice_name (pcmInfo) << "}"; } JUCE_ALSA_LOG ("Soundcard ID: " << id << ", name: '" << name << ", isInput:" << isInput << ", isOutput:" << isOutput << "\n"); if (isInput) { inputNames.add (name); inputIds.add (id); } if (isOutput) { outputNames.add (name); outputIds.add (id); } } } } JUCE_CHECKED_RESULT (snd_ctl_close (handle)); } } } /* Enumerates all ALSA output devices (as output by the command aplay -L) Does not try to open the devices (with "testDevice" for example), so that it also finds devices that are busy and not yet available. */ void enumerateAlsaPCMDevices() { void** hints = nullptr; if (JUCE_CHECKED_RESULT (snd_device_name_hint (-1, "pcm", &hints)) == 0) { for (char** h = (char**) hints; *h; ++h) { const String id (hintToString (*h, "NAME")); const String description (hintToString (*h, "DESC")); const String ioid (hintToString (*h, "IOID")); JUCE_ALSA_LOG ("ID: " << id << "; desc: " << description << "; ioid: " << ioid); String ss = id.fromFirstOccurrenceOf ("=", false, false) .upToFirstOccurrenceOf (",", false, false); if (id.isEmpty() || id.startsWith ("default:") || id.startsWith ("sysdefault:") || id.startsWith ("plughw:") || id == "null") continue; String name (description.replace ("\n", "; ")); if (name.isEmpty()) name = id; bool isOutput = (ioid != "Input"); bool isInput = (ioid != "Output"); // alsa is stupid here, it advertises dmix and dsnoop as input/output devices, but // opening dmix as input, or dsnoop as output will trigger errors.. isInput = isInput && ! id.startsWith ("dmix"); isOutput = isOutput && ! id.startsWith ("dsnoop"); if (isInput) { inputNames.add (name); inputIds.add (id); } if (isOutput) { outputNames.add (name); outputIds.add (id); } } snd_device_name_free_hint (hints); } // sometimes the "default" device is not listed, but it is nice to see it explicitely in the list if (! outputIds.contains ("default")) testDevice ("default", "Default ALSA Output", "Default ALSA Input"); // same for the pulseaudio plugin if (! outputIds.contains ("pulse")) testDevice ("pulse", "Pulseaudio output", "Pulseaudio input"); // make sure the default device is listed first, and followed by the pulse device (if present) int idx = outputIds.indexOf ("pulse"); outputIds.move (idx, 0); outputNames.move (idx, 0); idx = inputIds.indexOf ("pulse"); inputIds.move (idx, 0); inputNames.move (idx, 0); idx = outputIds.indexOf ("default"); outputIds.move (idx, 0); outputNames.move (idx, 0); idx = inputIds.indexOf ("default"); inputIds.move (idx, 0); inputNames.move (idx, 0); } static String hintToString (const void* hints, const char* type) { char* const hint = snd_device_name_get_hint (hints, type); const String s (String::fromUTF8 (hint)); ::free (hint); return s; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAAudioIODeviceType) }; } //============================================================================== AudioIODeviceType* createAudioIODeviceType_ALSA_Soundcards() { return new ALSAAudioIODeviceType (true, "ALSA HW"); } AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices() { return new ALSAAudioIODeviceType (false, "ALSA"); } AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return createAudioIODeviceType_ALSA_PCMDevices(); } juce_linux_AudioCDReader.cpp000066400000000000000000000035351320201440200341560ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioCDReader::AudioCDReader() : AudioFormatReader (0, "CD Audio") { } StringArray AudioCDReader::getAvailableCDNames() { StringArray names; return names; } AudioCDReader* AudioCDReader::createReaderForCD (const int index) { return nullptr; } AudioCDReader::~AudioCDReader() { } void AudioCDReader::refreshTrackLengths() { } bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { return false; } bool AudioCDReader::isCDStillPresent() const { return false; } bool AudioCDReader::isTrackAudio (int trackNum) const { return false; } void AudioCDReader::enableIndexScanning (bool b) { } int AudioCDReader::getLastIndex() const { return 0; } Array AudioCDReader::findIndexesInTrack (const int trackNumber) { return Array(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp000066400000000000000000000543711320201440200335000ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ //============================================================================== static void* juce_libjackHandle = nullptr; static void* juce_loadJackFunction (const char* const name) { if (juce_libjackHandle == nullptr) return nullptr; return dlsym (juce_libjackHandle, name); } #define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \ return_type fn_name argument_types \ { \ typedef return_type (*fn_type) argument_types; \ static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ return (fn != nullptr) ? ((*fn) arguments) : (return_type) 0; \ } #define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \ void fn_name argument_types \ { \ typedef void (*fn_type) argument_types; \ static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ if (fn != nullptr) (*fn) arguments; \ } //============================================================================== JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status)); JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client)); JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client)); JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client)); JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client)); JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client)); JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg)); JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes)); JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port)); JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size)); JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func)); JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg)); JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)); JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)); JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)); JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)); JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)); JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port)); JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name)); #if JUCE_DEBUG #define JACK_LOGGING_ENABLED 1 #endif #if JACK_LOGGING_ENABLED namespace { void jack_Log (const String& s) { std::cerr << s << std::endl; } const char* getJackErrorMessage (const jack_status_t status) { if (status & JackServerFailed || status & JackServerError) return "Unable to connect to JACK server"; if (status & JackVersionError) return "Client's protocol version does not match"; if (status & JackInvalidOption) return "The operation contained an invalid or unsupported option"; if (status & JackNameNotUnique) return "The desired client name was not unique"; if (status & JackNoSuchClient) return "Requested client does not exist"; if (status & JackInitFailure) return "Unable to initialize client"; return nullptr; } } #define JUCE_JACK_LOG_STATUS(x) { if (const char* m = getJackErrorMessage (x)) jack_Log (m); } #define JUCE_JACK_LOG(x) jack_Log(x) #else #define JUCE_JACK_LOG_STATUS(x) {} #define JUCE_JACK_LOG(x) {} #endif //============================================================================== #ifndef JUCE_JACK_CLIENT_NAME #define JUCE_JACK_CLIENT_NAME "JUCEJack" #endif struct JackPortIterator { JackPortIterator (jack_client_t* const client, const bool forInput) : ports (nullptr), index (-1) { if (client != nullptr) ports = juce::jack_get_ports (client, nullptr, nullptr, forInput ? JackPortIsOutput : JackPortIsInput); // (NB: This looks like it's the wrong way round, but it is correct!) } ~JackPortIterator() { ::free (ports); } bool next() { if (ports == nullptr || ports [index + 1] == nullptr) return false; name = CharPointer_UTF8 (ports[++index]); clientName = name.upToFirstOccurrenceOf (":", false, false); return true; } const char** ports; int index; String name; String clientName; }; class JackAudioIODeviceType; static Array activeDeviceTypes; //============================================================================== class JackAudioIODevice : public AudioIODevice { public: JackAudioIODevice (const String& deviceName, const String& inId, const String& outId) : AudioIODevice (deviceName, "JACK"), inputId (inId), outputId (outId), deviceIsOpen (false), callback (nullptr), totalNumberOfInputChannels (0), totalNumberOfOutputChannels (0) { jassert (deviceName.isNotEmpty()); jack_status_t status; client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status); if (client == nullptr) { JUCE_JACK_LOG_STATUS (status); } else { juce::jack_set_error_function (errorCallback); // open input ports const StringArray inputChannels (getInputChannelNames()); for (int i = 0; i < inputChannels.size(); ++i) { String inputName; inputName << "in_" << ++totalNumberOfInputChannels; inputPorts.add (juce::jack_port_register (client, inputName.toUTF8(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); } // open output ports const StringArray outputChannels (getOutputChannelNames()); for (int i = 0; i < outputChannels.size (); ++i) { String outputName; outputName << "out_" << ++totalNumberOfOutputChannels; outputPorts.add (juce::jack_port_register (client, outputName.toUTF8(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); } inChans.calloc (totalNumberOfInputChannels + 2); outChans.calloc (totalNumberOfOutputChannels + 2); } } ~JackAudioIODevice() { close(); if (client != nullptr) { juce::jack_client_close (client); client = nullptr; } } StringArray getChannelNames (bool forInput) const { StringArray names; for (JackPortIterator i (client, forInput); i.next();) if (i.clientName == getName()) names.add (i.name.fromFirstOccurrenceOf (":", false, false)); return names; } StringArray getOutputChannelNames() override { return getChannelNames (false); } StringArray getInputChannelNames() override { return getChannelNames (true); } Array getAvailableSampleRates() override { Array rates; if (client != nullptr) rates.add (juce::jack_get_sample_rate (client)); return rates; } Array getAvailableBufferSizes() override { Array sizes; if (client != nullptr) sizes.add (juce::jack_get_buffer_size (client)); return sizes; } int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); } int getCurrentBufferSizeSamples() override { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; } double getCurrentSampleRate() override { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double /* sampleRate */, int /* bufferSizeSamples */) override { if (client == nullptr) { lastError = "No JACK client running"; return lastError; } lastError.clear(); close(); juce::jack_set_process_callback (client, processCallback, this); juce::jack_set_port_connect_callback (client, portConnectCallback, this); juce::jack_on_shutdown (client, shutdownCallback, this); juce::jack_activate (client); deviceIsOpen = true; if (! inputChannels.isZero()) { for (JackPortIterator i (client, true); i.next();) { if (inputChannels [i.index] && i.clientName == getName()) { int error = juce::jack_connect (client, i.ports[i.index], juce::jack_port_name ((jack_port_t*) inputPorts[i.index])); if (error != 0) JUCE_JACK_LOG ("Cannot connect input port " + String (i.index) + " (" + i.name + "), error " + String (error)); } } } if (! outputChannels.isZero()) { for (JackPortIterator i (client, false); i.next();) { if (outputChannels [i.index] && i.clientName == getName()) { int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i.index]), i.ports[i.index]); if (error != 0) JUCE_JACK_LOG ("Cannot connect output port " + String (i.index) + " (" + i.name + "), error " + String (error)); } } } return lastError; } void close() override { stop(); if (client != nullptr) { juce::jack_deactivate (client); juce::jack_set_process_callback (client, processCallback, nullptr); juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr); juce::jack_on_shutdown (client, shutdownCallback, nullptr); } deviceIsOpen = false; } void start (AudioIODeviceCallback* newCallback) override { if (deviceIsOpen && newCallback != callback) { if (newCallback != nullptr) newCallback->audioDeviceAboutToStart (this); AudioIODeviceCallback* const oldCallback = callback; { const ScopedLock sl (callbackLock); callback = newCallback; } if (oldCallback != nullptr) oldCallback->audioDeviceStopped(); } } void stop() override { start (nullptr); } bool isOpen() override { return deviceIsOpen; } bool isPlaying() override { return callback != nullptr; } int getCurrentBitDepth() override { return 32; } String getLastError() override { return lastError; } BigInteger getActiveOutputChannels() const override { return activeOutputChannels; } BigInteger getActiveInputChannels() const override { return activeInputChannels; } int getOutputLatencyInSamples() override { int latency = 0; for (int i = 0; i < outputPorts.size(); i++) latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i])); return latency; } int getInputLatencyInSamples() override { int latency = 0; for (int i = 0; i < inputPorts.size(); i++) latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i])); return latency; } String inputId, outputId; private: void process (const int numSamples) { int numActiveInChans = 0, numActiveOutChans = 0; for (int i = 0; i < totalNumberOfInputChannels; ++i) { if (activeInputChannels[i]) if (jack_default_audio_sample_t* in = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples)) inChans [numActiveInChans++] = (float*) in; } for (int i = 0; i < totalNumberOfOutputChannels; ++i) { if (activeOutputChannels[i]) if (jack_default_audio_sample_t* out = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples)) outChans [numActiveOutChans++] = (float*) out; } const ScopedLock sl (callbackLock); if (callback != nullptr) { if ((numActiveInChans + numActiveOutChans) > 0) callback->audioDeviceIOCallback (const_cast (inChans.getData()), numActiveInChans, outChans, numActiveOutChans, numSamples); } else { for (int i = 0; i < numActiveOutChans; ++i) zeromem (outChans[i], sizeof (float) * numSamples); } } static int processCallback (jack_nframes_t nframes, void* callbackArgument) { if (callbackArgument != nullptr) ((JackAudioIODevice*) callbackArgument)->process (nframes); return 0; } void updateActivePorts() { BigInteger newOutputChannels, newInputChannels; for (int i = 0; i < outputPorts.size(); ++i) if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i))) newOutputChannels.setBit (i); for (int i = 0; i < inputPorts.size(); ++i) if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i))) newInputChannels.setBit (i); if (newOutputChannels != activeOutputChannels || newInputChannels != activeInputChannels) { AudioIODeviceCallback* const oldCallback = callback; stop(); activeOutputChannels = newOutputChannels; activeInputChannels = newInputChannels; if (oldCallback != nullptr) start (oldCallback); sendDeviceChangedCallback(); } } static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg) { if (JackAudioIODevice* device = static_cast (arg)) device->updateActivePorts(); } static void threadInitCallback (void* /* callbackArgument */) { JUCE_JACK_LOG ("JackAudioIODevice::initialise"); } static void shutdownCallback (void* callbackArgument) { JUCE_JACK_LOG ("JackAudioIODevice::shutdown"); if (JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument) { device->client = nullptr; device->close(); } } static void errorCallback (const char* msg) { JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg)); } static void sendDeviceChangedCallback(); bool deviceIsOpen; jack_client_t* client; String lastError; AudioIODeviceCallback* callback; CriticalSection callbackLock; HeapBlock inChans, outChans; int totalNumberOfInputChannels; int totalNumberOfOutputChannels; Array inputPorts, outputPorts; BigInteger activeInputChannels, activeOutputChannels; }; //============================================================================== class JackAudioIODeviceType : public AudioIODeviceType { public: JackAudioIODeviceType() : AudioIODeviceType ("JACK"), hasScanned (false) { activeDeviceTypes.add (this); } ~JackAudioIODeviceType() { activeDeviceTypes.removeFirstMatchingValue (this); } void scanForDevices() { hasScanned = true; inputNames.clear(); inputIds.clear(); outputNames.clear(); outputIds.clear(); if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY); if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); if (juce_libjackHandle == nullptr) return; jack_status_t status; // open a dummy client if (jack_client_t* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status)) { // scan for output devices for (JackPortIterator i (client, false); i.next();) { if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.clientName)) { inputNames.add (i.clientName); inputIds.add (i.ports [i.index]); } } // scan for input devices for (JackPortIterator i (client, true); i.next();) { if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.clientName)) { outputNames.add (i.clientName); outputIds.add (i.ports [i.index]); } } juce::jack_client_close (client); } else { JUCE_JACK_LOG_STATUS (status); } } StringArray getDeviceNames (bool wantInputNames) const { jassert (hasScanned); // need to call scanForDevices() before doing this return wantInputNames ? inputNames : outputNames; } int getDefaultDeviceIndex (bool /* forInput */) const { jassert (hasScanned); // need to call scanForDevices() before doing this return 0; } bool hasSeparateInputsAndOutputs() const { return true; } int getIndexOfDevice (AudioIODevice* device, bool asInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this if (JackAudioIODevice* d = dynamic_cast (device)) return asInput ? inputIds.indexOf (d->inputId) : outputIds.indexOf (d->outputId); return -1; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { jassert (hasScanned); // need to call scanForDevices() before doing this const int inputIndex = inputNames.indexOf (inputDeviceName); const int outputIndex = outputNames.indexOf (outputDeviceName); if (inputIndex >= 0 || outputIndex >= 0) return new JackAudioIODevice (outputIndex >= 0 ? outputDeviceName : inputDeviceName, inputIds [inputIndex], outputIds [outputIndex]); return nullptr; } void portConnectionChange() { callDeviceChangeListeners(); } private: StringArray inputNames, outputNames, inputIds, outputIds; bool hasScanned; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType) }; void JackAudioIODevice::sendDeviceChangedCallback() { for (int i = activeDeviceTypes.size(); --i >= 0;) if (JackAudioIODeviceType* d = activeDeviceTypes[i]) d->portConnectionChange(); } //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return new JackAudioIODeviceType(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp000066400000000000000000000451411320201440200325230ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_ALSA // You can define these strings in your app if you want to override the default names: #ifndef JUCE_ALSA_MIDI_INPUT_NAME #define JUCE_ALSA_MIDI_INPUT_NAME "Juce Midi Input" #endif #ifndef JUCE_ALSA_MIDI_OUTPUT_NAME #define JUCE_ALSA_MIDI_OUTPUT_NAME "Juce Midi Output" #endif //============================================================================== namespace { class AlsaPortAndCallback; //============================================================================== class AlsaClient : public ReferenceCountedObject { public: typedef ReferenceCountedObjectPtr Ptr; AlsaClient (bool forInput) : input (forInput), handle (nullptr) { snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT : SND_SEQ_OPEN_OUTPUT, 0); } ~AlsaClient() { if (handle != nullptr) { snd_seq_close (handle); handle = nullptr; } jassert (activeCallbacks.size() == 0); if (inputThread) { inputThread->stopThread (3000); inputThread = nullptr; } } bool isInput() const noexcept { return input; } void setName (const String& name) { snd_seq_set_client_name (handle, name.toUTF8()); } void registerCallback (AlsaPortAndCallback* cb) { if (cb != nullptr) { { const ScopedLock sl (callbackLock); activeCallbacks.add (cb); if (inputThread == nullptr) inputThread = new MidiInputThread (*this); } inputThread->startThread(); } } void unregisterCallback (AlsaPortAndCallback* cb) { const ScopedLock sl (callbackLock); jassert (activeCallbacks.contains (cb)); activeCallbacks.removeAllInstancesOf (cb); if (activeCallbacks.size() == 0 && inputThread->isThreadRunning()) inputThread->signalThreadShouldExit(); } void handleIncomingMidiMessage (const MidiMessage& message, int port); snd_seq_t* get() const noexcept { return handle; } private: bool input; snd_seq_t* handle; Array activeCallbacks; CriticalSection callbackLock; //============================================================================== class MidiInputThread : public Thread { public: MidiInputThread (AlsaClient& c) : Thread ("Juce MIDI Input"), client (c) { jassert (client.input && client.get() != nullptr); } void run() override { const int maxEventSize = 16 * 1024; snd_midi_event_t* midiParser; snd_seq_t* seqHandle = client.get(); if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) { const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); HeapBlock pfd ((size_t) numPfds); snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN); HeapBlock buffer (maxEventSize); while (! threadShouldExit()) { if (poll (pfd, (nfds_t) numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call { if (threadShouldExit()) break; snd_seq_nonblock (seqHandle, 1); do { snd_seq_event_t* inputEvent = nullptr; if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) { // xxx what about SYSEXes that are too big for the buffer? const long numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent); snd_midi_event_reset_decode (midiParser); if (numBytes > 0) { const MidiMessage message ((const uint8*) buffer, (int) numBytes, Time::getMillisecondCounter() * 0.001); client.handleIncomingMidiMessage (message, inputEvent->dest.port); } snd_seq_free_event (inputEvent); } } while (snd_seq_event_input_pending (seqHandle, 0) > 0); } } snd_midi_event_free (midiParser); } }; private: AlsaClient& client; }; ScopedPointer inputThread; }; static AlsaClient::Ptr globalAlsaSequencerIn() { static AlsaClient::Ptr global (new AlsaClient (true)); return global; } static AlsaClient::Ptr globalAlsaSequencerOut() { static AlsaClient::Ptr global (new AlsaClient (false)); return global; } static AlsaClient::Ptr globalAlsaSequencer (bool input) { return input ? globalAlsaSequencerIn() : globalAlsaSequencerOut(); } //============================================================================== // represents an input or output port of the supplied AlsaClient class AlsaPort { public: AlsaPort() noexcept : portId (-1) {} AlsaPort (const AlsaClient::Ptr& c, int port) noexcept : client (c), portId (port) {} void createPort (const AlsaClient::Ptr& c, const String& name, bool forInput) { client = c; if (snd_seq_t* handle = client->get()) portId = snd_seq_create_simple_port (handle, name.toUTF8(), forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), SND_SEQ_PORT_TYPE_MIDI_GENERIC); } void deletePort() { if (isValid()) { snd_seq_delete_simple_port (client->get(), portId); portId = -1; } } void connectWith (int sourceClient, int sourcePort) { if (client->isInput()) snd_seq_connect_from (client->get(), portId, sourceClient, sourcePort); else snd_seq_connect_to (client->get(), portId, sourceClient, sourcePort); } bool isValid() const noexcept { return client != nullptr && client->get() != nullptr && portId >= 0; } AlsaClient::Ptr client; int portId; }; //============================================================================== class AlsaPortAndCallback { public: AlsaPortAndCallback (AlsaPort p, MidiInput* in, MidiInputCallback* cb) : port (p), midiInput (in), callback (cb), callbackEnabled (false) { } ~AlsaPortAndCallback() { enableCallback (false); port.deletePort(); } void enableCallback (bool enable) { if (callbackEnabled != enable) { callbackEnabled = enable; if (enable) port.client->registerCallback (this); else port.client->unregisterCallback (this); } } void handleIncomingMidiMessage (const MidiMessage& message) const { callback->handleIncomingMidiMessage (midiInput, message); } private: AlsaPort port; MidiInput* midiInput; MidiInputCallback* callback; bool callbackEnabled; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlsaPortAndCallback) }; void AlsaClient::handleIncomingMidiMessage (const MidiMessage& message, int port) { const ScopedLock sl (callbackLock); if (AlsaPortAndCallback* const cb = activeCallbacks[port]) cb->handleIncomingMidiMessage (message); } //============================================================================== static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq, snd_seq_client_info_t* clientInfo, const bool forInput, StringArray& deviceNamesFound, const int deviceIndexToOpen) { AlsaPort port; snd_seq_t* seqHandle = seq->get(); snd_seq_port_info_t* portInfo = nullptr; if (snd_seq_port_info_malloc (&portInfo) == 0) { int numPorts = snd_seq_client_info_get_num_ports (clientInfo); const int client = snd_seq_client_info_get_client (clientInfo); snd_seq_port_info_set_client (portInfo, client); snd_seq_port_info_set_port (portInfo, -1); while (--numPorts >= 0) { if (snd_seq_query_next_port (seqHandle, portInfo) == 0 && (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_READ : SND_SEQ_PORT_CAP_WRITE)) != 0) { deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); if (deviceNamesFound.size() == deviceIndexToOpen + 1) { const int sourcePort = snd_seq_port_info_get_port (portInfo); const int sourceClient = snd_seq_client_info_get_client (clientInfo); if (sourcePort != -1) { const String name (forInput ? JUCE_ALSA_MIDI_INPUT_NAME : JUCE_ALSA_MIDI_OUTPUT_NAME); seq->setName (name); port.createPort (seq, name, forInput); port.connectWith (sourceClient, sourcePort); } } } } snd_seq_port_info_free (portInfo); } return port; } static AlsaPort iterateMidiDevices (const bool forInput, StringArray& deviceNamesFound, const int deviceIndexToOpen) { AlsaPort port; const AlsaClient::Ptr client (globalAlsaSequencer (forInput)); if (snd_seq_t* const seqHandle = client->get()) { snd_seq_system_info_t* systemInfo = nullptr; snd_seq_client_info_t* clientInfo = nullptr; if (snd_seq_system_info_malloc (&systemInfo) == 0) { if (snd_seq_system_info (seqHandle, systemInfo) == 0 && snd_seq_client_info_malloc (&clientInfo) == 0) { int numClients = snd_seq_system_info_get_cur_clients (systemInfo); while (--numClients >= 0 && ! port.isValid()) if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) port = iterateMidiClient (client, clientInfo, forInput, deviceNamesFound, deviceIndexToOpen); snd_seq_client_info_free (clientInfo); } snd_seq_system_info_free (systemInfo); } } deviceNamesFound.appendNumbersToDuplicates (true, true); return port; } AlsaPort createMidiDevice (const bool forInput, const String& deviceNameToOpen) { AlsaPort port; AlsaClient::Ptr client (new AlsaClient (forInput)); if (client->get()) { client->setName (deviceNameToOpen + (forInput ? " Input" : " Output")); port.createPort (client, forInput ? "in" : "out", forInput); } return port; } //============================================================================== class MidiOutputDevice { public: MidiOutputDevice (MidiOutput* const output, const AlsaPort& p) : midiOutput (output), port (p), maxEventSize (16 * 1024) { jassert (port.isValid() && midiOutput != nullptr); snd_midi_event_new ((size_t) maxEventSize, &midiParser); } ~MidiOutputDevice() { snd_midi_event_free (midiParser); port.deletePort(); } bool sendMessageNow (const MidiMessage& message) { if (message.getRawDataSize() > maxEventSize) { maxEventSize = message.getRawDataSize(); snd_midi_event_free (midiParser); snd_midi_event_new ((size_t) maxEventSize, &midiParser); } snd_seq_event_t event; snd_seq_ev_clear (&event); long numBytes = (long) message.getRawDataSize(); const uint8* data = message.getRawData(); snd_seq_t* seqHandle = port.client->get(); bool success = true; while (numBytes > 0) { const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event); if (numSent <= 0) { success = numSent == 0; break; } numBytes -= numSent; data += numSent; snd_seq_ev_set_source (&event, 0); snd_seq_ev_set_subs (&event); snd_seq_ev_set_direct (&event); if (snd_seq_event_output_direct (seqHandle, &event) < 0) { success = false; break; } } snd_midi_event_reset_encode (midiParser); return success; } private: MidiOutput* const midiOutput; AlsaPort port; snd_midi_event_t* midiParser; int maxEventSize; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice); }; } // namespace StringArray MidiOutput::getDevices() { StringArray devices; iterateMidiDevices (false, devices, -1); return devices; } int MidiOutput::getDefaultDeviceIndex() { return 0; } MidiOutput* MidiOutput::openDevice (int deviceIndex) { MidiOutput* newDevice = nullptr; StringArray devices; AlsaPort port (iterateMidiDevices (false, devices, deviceIndex)); if (port.isValid()) { newDevice = new MidiOutput(); newDevice->internal = new MidiOutputDevice (newDevice, port); } return newDevice; } MidiOutput* MidiOutput::createNewDevice (const String& deviceName) { MidiOutput* newDevice = nullptr; AlsaPort port (createMidiDevice (false, deviceName)); if (port.isValid()) { newDevice = new MidiOutput(); newDevice->internal = new MidiOutputDevice (newDevice, port); } return newDevice; } MidiOutput::~MidiOutput() { stopBackgroundThread(); delete static_cast (internal); } void MidiOutput::sendMessageNow (const MidiMessage& message) { static_cast (internal)->sendMessageNow (message); } //============================================================================== MidiInput::MidiInput (const String& nm) : name (nm), internal (nullptr) { } MidiInput::~MidiInput() { stop(); delete static_cast (internal); } void MidiInput::start() { static_cast (internal)->enableCallback (true); } void MidiInput::stop() { static_cast (internal)->enableCallback (false); } int MidiInput::getDefaultDeviceIndex() { return 0; } StringArray MidiInput::getDevices() { StringArray devices; iterateMidiDevices (true, devices, -1); return devices; } MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) { MidiInput* newDevice = nullptr; StringArray devices; AlsaPort port (iterateMidiDevices (true, devices, deviceIndex)); if (port.isValid()) { newDevice = new MidiInput (devices [deviceIndex]); newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback); } return newDevice; } MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) { MidiInput* newDevice = nullptr; AlsaPort port (createMidiDevice (true, deviceName)); if (port.isValid()) { newDevice = new MidiInput (deviceName); newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback); } return newDevice; } //============================================================================== #else // (These are just stub functions if ALSA is unavailable...) StringArray MidiOutput::getDevices() { return StringArray(); } int MidiOutput::getDefaultDeviceIndex() { return 0; } MidiOutput* MidiOutput::openDevice (int) { return nullptr; } MidiOutput* MidiOutput::createNewDevice (const String&) { return nullptr; } MidiOutput::~MidiOutput() {} void MidiOutput::sendMessageNow (const MidiMessage&) {} MidiInput::MidiInput (const String& nm) : name (nm), internal (nullptr) {} MidiInput::~MidiInput() {} void MidiInput::start() {} void MidiInput::stop() {} int MidiInput::getDefaultDeviceIndex() { return 0; } StringArray MidiInput::getDevices() { return StringArray(); } MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return nullptr; } MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return nullptr; } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm000066400000000000000000000363411320201440200335210ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ const int kilobytesPerSecond1x = 176; struct AudioTrackProducerClass : public ObjCClass { AudioTrackProducerClass() : ObjCClass ("JUCEAudioTrackProducer_") { addIvar ("source"); addMethod (@selector (initWithAudioSourceHolder:), initWithAudioSourceHolder, "@@:^v"); addMethod (@selector (cleanupTrackAfterBurn:), cleanupTrackAfterBurn, "v@:@"); addMethod (@selector (cleanupTrackAfterVerification:), cleanupTrackAfterVerification, "c@:@"); addMethod (@selector (estimateLengthOfTrack:), estimateLengthOfTrack, "Q@:@"); addMethod (@selector (prepareTrack:forBurn:toMedia:), prepareTrack, "c@:@@@"); addMethod (@selector (prepareTrackForVerification:), prepareTrackForVerification, "c@:@"); addMethod (@selector (produceDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), produceDataForTrack, "I@:@^cIQI^I"); addMethod (@selector (producePreGapForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), produceDataForTrack, "I@:@^cIQI^I"); addMethod (@selector (verifyDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), produceDataForTrack, "I@:@^cIQI^I"); registerClass(); } struct AudioSourceHolder { AudioSourceHolder (AudioSource* s, int numFrames) : source (s), readPosition (0), lengthInFrames (numFrames) { } ~AudioSourceHolder() { if (source != nullptr) source->releaseResources(); } ScopedPointer source; int readPosition, lengthInFrames; }; private: static id initWithAudioSourceHolder (id self, SEL, AudioSourceHolder* source) { self = sendSuperclassMessage (self, @selector (init)); object_setInstanceVariable (self, "source", source); return self; } static AudioSourceHolder* getSource (id self) { return getIvar (self, "source"); } static void dealloc (id self, SEL) { delete getSource (self); sendSuperclassMessage (self, @selector (dealloc)); } static void cleanupTrackAfterBurn (id self, SEL, DRTrack*) {} static BOOL cleanupTrackAfterVerification (id self, SEL, DRTrack*) { return true; } static uint64_t estimateLengthOfTrack (id self, SEL, DRTrack*) { return getSource (self)->lengthInFrames; } static BOOL prepareTrack (id self, SEL, DRTrack*, DRBurn*, NSDictionary*) { if (AudioSourceHolder* const source = getSource (self)) { source->source->prepareToPlay (44100 / 75, 44100); source->readPosition = 0; } return true; } static BOOL prepareTrackForVerification (id self, SEL, DRTrack*) { if (AudioSourceHolder* const source = getSource (self)) source->source->prepareToPlay (44100 / 75, 44100); return true; } static uint32_t produceDataForTrack (id self, SEL, DRTrack*, char* buffer, uint32_t bufferLength, uint64_t /*address*/, uint32_t /*blockSize*/, uint32_t* /*flags*/) { if (AudioSourceHolder* const source = getSource (self)) { const int numSamples = jmin ((int) bufferLength / 4, (source->lengthInFrames * (44100 / 75)) - source->readPosition); if (numSamples > 0) { AudioSampleBuffer tempBuffer (2, numSamples); AudioSourceChannelInfo info (tempBuffer); source->source->getNextAudioBlock (info); typedef AudioData::Pointer CDSampleFormat; typedef AudioData::Pointer SourceSampleFormat; CDSampleFormat left (buffer, 2); left.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (0)), numSamples); CDSampleFormat right (buffer + 2, 2); right.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (1)), numSamples); source->readPosition += numSamples; } return numSamples * 4; } return 0; } static uint32_t producePreGapForTrack (id self, SEL, DRTrack*, char* buffer, uint32_t bufferLength, uint64_t /*address*/, uint32_t /*blockSize*/, uint32_t* /*flags*/) { zeromem (buffer, bufferLength); return bufferLength; } static BOOL verifyDataForTrack (id self, SEL, DRTrack*, const char*, uint32_t /*bufferLength*/, uint64_t /*address*/, uint32_t /*blockSize*/, uint32_t* /*flags*/) { return true; } }; struct OpenDiskDevice { OpenDiskDevice (DRDevice* d) : device (d), tracks ([[NSMutableArray alloc] init]), underrunProtection (true) { } ~OpenDiskDevice() { [tracks release]; } void addSourceTrack (AudioSource* source, int numSamples) { if (source != nullptr) { const int numFrames = (numSamples + 587) / 588; static AudioTrackProducerClass cls; NSObject* producer = [cls.createInstance() performSelector: @selector (initWithAudioSourceHolder:) withObject: (id) new AudioTrackProducerClass::AudioSourceHolder (source, numFrames)]; DRTrack* track = [[DRTrack alloc] initWithProducer: producer]; { NSMutableDictionary* p = [[track properties] mutableCopy]; [p setObject: [DRMSF msfWithFrames: numFrames] forKey: DRTrackLengthKey]; [p setObject: [NSNumber numberWithUnsignedShort: 2352] forKey: DRBlockSizeKey]; [p setObject: [NSNumber numberWithInt: 0] forKey: DRDataFormKey]; [p setObject: [NSNumber numberWithInt: 0] forKey: DRBlockTypeKey]; [p setObject: [NSNumber numberWithInt: 0] forKey: DRTrackModeKey]; [p setObject: [NSNumber numberWithInt: 0] forKey: DRSessionFormatKey]; [track setProperties: p]; [p release]; } [tracks addObject: track]; [track release]; [producer release]; } } String burn (AudioCDBurner::BurnProgressListener* listener, bool shouldEject, bool peformFakeBurnForTesting, int burnSpeed) { DRBurn* burn = [DRBurn burnForDevice: device]; if (! [device acquireExclusiveAccess]) return "Couldn't open or write to the CD device"; [device acquireMediaReservation]; NSMutableDictionary* d = [[burn properties] mutableCopy]; [d autorelease]; [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; [d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) forKey: DRBurnCompletionActionKey]; if (burnSpeed > 0) [d setObject: [NSNumber numberWithFloat: burnSpeed * kilobytesPerSecond1x] forKey: DRBurnRequestedSpeedKey]; if (! underrunProtection) [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnUnderrunProtectionKey]; [burn setProperties: d]; [burn writeLayout: tracks]; for (;;) { Thread::sleep (300); float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue]; if (listener != nullptr && listener->audioCDBurnProgress (progress)) { [burn abort]; return "User cancelled the write operation"; } if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed]) return "Write operation failed"; if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone]) break; NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] objectForKey: DRErrorStatusErrorStringKey]; if ([err length] > 0) return nsStringToJuce (err); } [device releaseMediaReservation]; [device releaseExclusiveAccess]; return String::empty; } DRDevice* device; NSMutableArray* tracks; bool underrunProtection; }; //============================================================================== class AudioCDBurner::Pimpl : public Timer { public: Pimpl (AudioCDBurner& b, int deviceIndex) : owner (b) { if (DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex]) { device = new OpenDiskDevice (dev); lastState = getDiskState(); startTimer (1000); } } ~Pimpl() { stopTimer(); } void timerCallback() override { const DiskState state = getDiskState(); if (state != lastState) { lastState = state; owner.sendChangeMessage(); } } DiskState getDiskState() const { if ([device->device isValid]) { NSDictionary* status = [device->device status]; NSString* state = [status objectForKey: DRDeviceMediaStateKey]; if ([state isEqualTo: DRDeviceMediaStateNone]) { if ([[status objectForKey: DRDeviceIsTrayOpenKey] boolValue]) return trayOpen; return noDisc; } if ([state isEqualTo: DRDeviceMediaStateMediaPresent]) { if ([[[status objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue] > 0) return writableDiskPresent; return readOnlyDiskPresent; } } return unknown; } bool openTray() { return [device->device isValid] && [device->device ejectMedia]; } Array getAvailableWriteSpeeds() const { Array results; if ([device->device isValid]) for (id kbPerSec in [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey]) results.add ([kbPerSec intValue] / kilobytesPerSecond1x); return results; } bool setBufferUnderrunProtection (const bool shouldBeEnabled) { if ([device->device isValid]) { device->underrunProtection = shouldBeEnabled; return shouldBeEnabled && [[[device->device status] objectForKey: DRDeviceCanUnderrunProtectCDKey] boolValue]; } return false; } int getNumAvailableAudioBlocks() const { return [[[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue]; } ScopedPointer device; private: DiskState lastState; AudioCDBurner& owner; }; //============================================================================== AudioCDBurner::AudioCDBurner (const int deviceIndex) { pimpl = new Pimpl (*this, deviceIndex); } AudioCDBurner::~AudioCDBurner() { } AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) { ScopedPointer b (new AudioCDBurner (deviceIndex)); if (b->pimpl->device == nil) b = nullptr; return b.release(); } StringArray AudioCDBurner::findAvailableDevices() { StringArray s; for (NSDictionary* dic in [DRDevice devices]) if (NSString* name = [dic valueForKey: DRDeviceProductNameKey]) s.add (nsStringToJuce (name)); return s; } AudioCDBurner::DiskState AudioCDBurner::getDiskState() const { return pimpl->getDiskState(); } bool AudioCDBurner::isDiskPresent() const { return getDiskState() == writableDiskPresent; } bool AudioCDBurner::openTray() { return pimpl->openTray(); } AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds) { const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds; DiskState oldState = getDiskState(); DiskState newState = oldState; while (newState == oldState && Time::currentTimeMillis() < timeout) { newState = getDiskState(); Thread::sleep (100); } return newState; } Array AudioCDBurner::getAvailableWriteSpeeds() const { return pimpl->getAvailableWriteSpeeds(); } bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled) { return pimpl->setBufferUnderrunProtection (shouldBeEnabled); } int AudioCDBurner::getNumAvailableAudioBlocks() const { return pimpl->getNumAvailableAudioBlocks(); } bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps) { if ([pimpl->device->device isValid]) { pimpl->device->addSourceTrack (source, numSamps); return true; } return false; } String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards, bool performFakeBurnForTesting, int writeSpeed) { if ([pimpl->device->device isValid]) return pimpl->device->burn (listener, ejectDiscAfterwards, performFakeBurnForTesting, writeSpeed); return "Couldn't open or write to the CD device"; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm000066400000000000000000000174651320201440200334740ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace CDReaderHelpers { inline const XmlElement* getElementForKey (const XmlElement& xml, const String& key) { forEachXmlChildElementWithTagName (xml, child, "key") if (child->getAllSubText().trim() == key) return child->getNextElement(); return nullptr; } static int getIntValueForKey (const XmlElement& xml, const String& key, int defaultValue = -1) { const XmlElement* const block = getElementForKey (xml, key); return block != nullptr ? block->getAllSubText().trim().getIntValue() : defaultValue; } // Get the track offsets for a CD given an XmlElement representing its TOC.Plist. // Returns NULL on success, otherwise a const char* representing an error. static const char* getTrackOffsets (XmlDocument& xmlDocument, Array& offsets) { const ScopedPointer xml (xmlDocument.getDocumentElement()); if (xml == nullptr) return "Couldn't parse XML in file"; const XmlElement* const dict = xml->getChildByName ("dict"); if (dict == nullptr) return "Couldn't get top level dictionary"; const XmlElement* const sessions = getElementForKey (*dict, "Sessions"); if (sessions == nullptr) return "Couldn't find sessions key"; const XmlElement* const session = sessions->getFirstChildElement(); if (session == nullptr) return "Couldn't find first session"; const int leadOut = getIntValueForKey (*session, "Leadout Block"); if (leadOut < 0) return "Couldn't find Leadout Block"; const XmlElement* const trackArray = getElementForKey (*session, "Track Array"); if (trackArray == nullptr) return "Couldn't find Track Array"; forEachXmlChildElement (*trackArray, track) { const int trackValue = getIntValueForKey (*track, "Start Block"); if (trackValue < 0) return "Couldn't find Start Block in the track"; offsets.add (trackValue * AudioCDReader::samplesPerFrame - 88200); } offsets.add (leadOut * AudioCDReader::samplesPerFrame - 88200); return nullptr; } static void findDevices (Array& cds) { File volumes ("/Volumes"); volumes.findChildFiles (cds, File::findDirectories, false); for (int i = cds.size(); --i >= 0;) if (! cds.getReference(i).getChildFile (".TOC.plist").exists()) cds.remove (i); } struct TrackSorter { static int getCDTrackNumber (const File& file) { return file.getFileName().initialSectionContainingOnly ("0123456789").getIntValue(); } static int compareElements (const File& first, const File& second) { const int firstTrack = getCDTrackNumber (first); const int secondTrack = getCDTrackNumber (second); jassert (firstTrack > 0 && secondTrack > 0); return firstTrack - secondTrack; } }; } //============================================================================== StringArray AudioCDReader::getAvailableCDNames() { Array cds; CDReaderHelpers::findDevices (cds); StringArray names; for (int i = 0; i < cds.size(); ++i) names.add (cds.getReference(i).getFileName()); return names; } AudioCDReader* AudioCDReader::createReaderForCD (const int index) { Array cds; CDReaderHelpers::findDevices (cds); if (cds[index].exists()) return new AudioCDReader (cds[index]); return nullptr; } AudioCDReader::AudioCDReader (const File& volume) : AudioFormatReader (0, "CD Audio"), volumeDir (volume), currentReaderTrack (-1), reader (0) { sampleRate = 44100.0; bitsPerSample = 16; numChannels = 2; usesFloatingPointData = false; refreshTrackLengths(); } AudioCDReader::~AudioCDReader() { } void AudioCDReader::refreshTrackLengths() { tracks.clear(); trackStartSamples.clear(); lengthInSamples = 0; volumeDir.findChildFiles (tracks, File::findFiles | File::ignoreHiddenFiles, false, "*.aiff"); CDReaderHelpers::TrackSorter sorter; tracks.sort (sorter); const File toc (volumeDir.getChildFile (".TOC.plist")); if (toc.exists()) { XmlDocument doc (toc); const char* error = CDReaderHelpers::getTrackOffsets (doc, trackStartSamples); (void) error; // could be logged.. lengthInSamples = trackStartSamples.getLast() - trackStartSamples.getFirst(); } } bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { while (numSamples > 0) { int track = -1; for (int i = 0; i < trackStartSamples.size() - 1; ++i) { if (startSampleInFile < trackStartSamples.getUnchecked (i + 1)) { track = i; break; } } if (track < 0) return false; if (track != currentReaderTrack) { reader = nullptr; if (FileInputStream* const in = tracks [track].createInputStream()) { BufferedInputStream* const bin = new BufferedInputStream (in, 65536, true); AiffAudioFormat format; reader = format.createReaderFor (bin, true); if (reader == nullptr) currentReaderTrack = -1; else currentReaderTrack = track; } } if (reader == nullptr) return false; const int startPos = (int) (startSampleInFile - trackStartSamples.getUnchecked (track)); const int numAvailable = (int) jmin ((int64) numSamples, reader->lengthInSamples - startPos); reader->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer, startPos, numAvailable); numSamples -= numAvailable; startSampleInFile += numAvailable; } return true; } bool AudioCDReader::isCDStillPresent() const { return volumeDir.exists(); } void AudioCDReader::ejectDisk() { JUCE_AUTORELEASEPOOL { [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())]; } } bool AudioCDReader::isTrackAudio (int trackNum) const { return tracks [trackNum].hasFileExtension (".aiff"); } void AudioCDReader::enableIndexScanning (bool) { // any way to do this on a Mac?? } int AudioCDReader::getLastIndex() const { return 0; } Array AudioCDReader::findIndexesInTrack (const int /*trackNumber*/) { return Array(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp000066400000000000000000002010221320201440200331040ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_COREAUDIO_LOGGING_ENABLED #define JUCE_COREAUDIOLOG(a) { String camsg ("CoreAudio: "); camsg << a; Logger::writeToLog (camsg); } #else #define JUCE_COREAUDIOLOG(a) #endif //============================================================================== struct SystemVol { SystemVol (AudioObjectPropertySelector selector) : outputDeviceID (kAudioObjectUnknown) { addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mElement = kAudioObjectPropertyElementMaster; addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; if (AudioHardwareServiceHasProperty (kAudioObjectSystemObject, &addr)) { UInt32 deviceIDSize = sizeof (outputDeviceID); OSStatus status = AudioHardwareServiceGetPropertyData (kAudioObjectSystemObject, &addr, 0, nullptr, &deviceIDSize, &outputDeviceID); if (status == noErr) { addr.mElement = kAudioObjectPropertyElementMaster; addr.mSelector = selector; addr.mScope = kAudioDevicePropertyScopeOutput; if (! AudioHardwareServiceHasProperty (outputDeviceID, &addr)) outputDeviceID = kAudioObjectUnknown; } } } float getGain() { Float32 gain = 0; if (outputDeviceID != kAudioObjectUnknown) { UInt32 size = sizeof (gain); AudioHardwareServiceGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &gain); } return (float) gain; } bool setGain (float gain) { if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) { Float32 newVolume = gain; UInt32 size = sizeof (newVolume); return AudioHardwareServiceSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newVolume) == noErr; } return false; } bool isMuted() { UInt32 muted = 0; if (outputDeviceID != kAudioObjectUnknown) { UInt32 size = sizeof (muted); AudioHardwareServiceGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &muted); } return muted != 0; } bool setMuted (bool mute) { if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) { UInt32 newMute = mute ? 1 : 0; UInt32 size = sizeof (newMute); return AudioHardwareServiceSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newMute) == noErr; } return false; } private: AudioDeviceID outputDeviceID; AudioObjectPropertyAddress addr; bool canSetVolume() { Boolean isSettable = NO; return AudioHardwareServiceIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr && isSettable; } }; #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 float JUCE_CALLTYPE SystemAudioVolume::getGain() { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).getGain(); } bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).setGain (gain); } bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return SystemVol (kAudioDevicePropertyMute).isMuted(); } bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return SystemVol (kAudioDevicePropertyMute).setMuted (mute); } //============================================================================== struct CoreAudioClasses { class CoreAudioIODevice; //============================================================================== class CoreAudioInternal : private Timer { public: CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id) : owner (d), inputLatency (0), outputLatency (0), bitDepth (32), callback (nullptr), #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 audioProcID (0), #endif deviceID (id), started (false), sampleRate (0), bufferSize (512), numInputChans (0), numOutputChans (0), callbacksAllowed (true) { jassert (deviceID != 0); updateDetailsFromDevice(); AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this); } ~CoreAudioInternal() { AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectRemovePropertyListener (deviceID, &pa, deviceListenerProc, this); stop (false); } void allocateTempBuffers() { const int tempBufSize = bufferSize + 4; audioBuffer.calloc ((size_t) ((numInputChans + numOutputChans) * tempBufSize)); tempInputBuffers.calloc ((size_t) numInputChans + 2); tempOutputBuffers.calloc ((size_t) numOutputChans + 2); int count = 0; for (int i = 0; i < numInputChans; ++i) tempInputBuffers[i] = audioBuffer + count++ * tempBufSize; for (int i = 0; i < numOutputChans; ++i) tempOutputBuffers[i] = audioBuffer + count++ * tempBufSize; } struct CallbackDetailsForChannel { int streamNum; int dataOffsetSamples; int dataStrideSamples; }; // returns the number of actual available channels StringArray getChannelInfo (const bool input, Array& newChannelInfo) const { StringArray newNames; int chanNum = 0; UInt32 size; AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyStreamConfiguration; pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) { HeapBlock bufList; bufList.calloc (size, 1); if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList))) { const int numStreams = (int) bufList->mNumberBuffers; for (int i = 0; i < numStreams; ++i) { const AudioBuffer& b = bufList->mBuffers[i]; for (unsigned int j = 0; j < b.mNumberChannels; ++j) { String name; NSString* nameNSString = nil; size = sizeof (nameNSString); pa.mSelector = kAudioObjectPropertyElementName; pa.mElement = (AudioObjectPropertyElement) chanNum + 1; if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr) { name = nsStringToJuce (nameNSString); [nameNSString release]; } if ((input ? activeInputChans : activeOutputChans) [chanNum]) { CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels }; newChannelInfo.add (info); } if (name.isEmpty()) name << (input ? "Input " : "Output ") << (chanNum + 1); newNames.add (name); ++chanNum; } } } } return newNames; } Array getSampleRatesFromDevice() const { Array newSampleRates; AudioObjectPropertyAddress pa; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; UInt32 size = 0; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) { HeapBlock ranges; ranges.calloc (size, 1); if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) { static const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 384000.0 }; for (int i = 0; i < numElementsInArray (possibleRates); ++i) { for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) { if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) { newSampleRates.add (possibleRates[i]); break; } } } } } if (newSampleRates.size() == 0 && sampleRate > 0) newSampleRates.add (sampleRate); return newSampleRates; } Array getBufferSizesFromDevice() const { Array newBufferSizes; AudioObjectPropertyAddress pa; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; UInt32 size = 0; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) { HeapBlock ranges; ranges.calloc (size, 1); if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) { newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); for (int i = 32; i < 2048; i += 32) { for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) { if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) { newBufferSizes.addIfNotAlreadyThere (i); break; } } } if (bufferSize > 0) newBufferSizes.addIfNotAlreadyThere (bufferSize); } } if (newBufferSizes.size() == 0 && bufferSize > 0) newBufferSizes.add (bufferSize); return newBufferSizes; } int getLatencyFromDevice (AudioObjectPropertyScope scope) const { UInt32 lat = 0; UInt32 size = sizeof (lat); AudioObjectPropertyAddress pa; pa.mElement = kAudioObjectPropertyElementMaster; pa.mSelector = kAudioDevicePropertyLatency; pa.mScope = scope; AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &lat); return (int) lat; } int getBitDepthFromDevice (AudioObjectPropertyScope scope) const { AudioObjectPropertyAddress pa; pa.mElement = kAudioObjectPropertyElementMaster; pa.mSelector = kAudioStreamPropertyPhysicalFormat; pa.mScope = scope; AudioStreamBasicDescription asbd; UInt32 size = sizeof (asbd); if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &asbd))) return (int) asbd.mBitsPerChannel; return 0; } void updateDetailsFromDevice() { stopTimer(); if (deviceID == 0) return; // this collects all the new details from the device without any locking, then // locks + swaps them afterwards. AudioObjectPropertyAddress pa; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; UInt32 isAlive; UInt32 size = sizeof (isAlive); pa.mSelector = kAudioDevicePropertyDeviceIsAlive; if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) && isAlive == 0) return; Float64 sr; size = sizeof (sr); pa.mSelector = kAudioDevicePropertyNominalSampleRate; if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr))) sampleRate = sr; UInt32 framesPerBuf = (UInt32) bufferSize; size = sizeof (framesPerBuf); pa.mSelector = kAudioDevicePropertyBufferFrameSize; AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); Array newBufferSizes (getBufferSizesFromDevice()); Array newSampleRates (getSampleRatesFromDevice()); inputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput); outputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput); Array newInChans, newOutChans; StringArray newInNames (getChannelInfo (true, newInChans)); StringArray newOutNames (getChannelInfo (false, newOutChans)); const int inputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeInput); const int outputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeOutput); bitDepth = jmax (inputBitDepth, outputBitDepth); if (bitDepth <= 0) bitDepth = 32; // after getting the new values, lock + apply them const ScopedLock sl (callbackLock); bufferSize = (int) framesPerBuf; allocateTempBuffers(); sampleRates.swapWith (newSampleRates); bufferSizes.swapWith (newBufferSizes); inChanNames.swapWith (newInNames); outChanNames.swapWith (newOutNames); inputChannelInfo.swapWith (newInChans); outputChannelInfo.swapWith (newOutChans); } //============================================================================== StringArray getSources (bool input) { StringArray s; HeapBlock types; const int num = getAllDataSourcesForDevice (deviceID, types); for (int i = 0; i < num; ++i) { AudioValueTranslation avt; char buffer[256]; avt.mInputData = &(types[i]); avt.mInputDataSize = sizeof (UInt32); avt.mOutputData = buffer; avt.mOutputDataSize = 256; UInt32 transSize = sizeof (avt); AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyDataSourceNameForID; pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &transSize, &avt))) s.add (buffer); } return s; } int getCurrentSourceIndex (bool input) const { OSType currentSourceID = 0; UInt32 size = sizeof (currentSourceID); int result = -1; AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyDataSource; pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; if (deviceID != 0) { if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ¤tSourceID))) { HeapBlock types; const int num = getAllDataSourcesForDevice (deviceID, types); for (int i = 0; i < num; ++i) { if (types[num] == currentSourceID) { result = i; break; } } } } return result; } void setCurrentSourceIndex (int index, bool input) { if (deviceID != 0) { HeapBlock types; const int num = getAllDataSourcesForDevice (deviceID, types); if (isPositiveAndBelow (index, num)) { AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyDataSource; pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; OSType typeId = types[index]; OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (typeId), &typeId)); } } } //============================================================================== String reopen (const BigInteger& inputChannels, const BigInteger& outputChannels, double newSampleRate, int bufferSizeSamples) { String error; callbacksAllowed = false; stopTimer(); stop (false); activeInputChans = inputChannels; activeInputChans.setRange (inChanNames.size(), activeInputChans.getHighestBit() + 1 - inChanNames.size(), false); activeOutputChans = outputChannels; activeOutputChans.setRange (outChanNames.size(), activeOutputChans.getHighestBit() + 1 - outChanNames.size(), false); numInputChans = activeInputChans.countNumberOfSetBits(); numOutputChans = activeOutputChans.countNumberOfSetBits(); // set sample rate AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyNominalSampleRate; pa.mScope = kAudioObjectPropertyScopeGlobal; pa.mElement = kAudioObjectPropertyElementMaster; Float64 sr = newSampleRate; if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr))) { error = "Couldn't change sample rate"; } else { // change buffer size UInt32 framesPerBuf = (UInt32) bufferSizeSamples; pa.mSelector = kAudioDevicePropertyBufferFrameSize; if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (framesPerBuf), &framesPerBuf))) { error = "Couldn't change buffer size"; } else { // Annoyingly, after changing the rate and buffer size, some devices fail to // correctly report their new settings until some random time in the future, so // after calling updateDetailsFromDevice, we need to manually bodge these values // to make sure we're using the correct numbers.. updateDetailsFromDevice(); sampleRate = newSampleRate; bufferSize = bufferSizeSamples; if (sampleRates.size() == 0) error = "Device has no available sample-rates"; else if (bufferSizes.size() == 0) error = "Device has no available buffer-sizes"; } } callbacksAllowed = true; return error; } bool start() { if (! started) { callback = nullptr; if (deviceID != 0) { #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (OK (AudioDeviceAddIOProc (deviceID, audioIOProc, this))) #else if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, this, &audioProcID))) #endif { if (OK (AudioDeviceStart (deviceID, audioIOProc))) { started = true; } else { #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); #else OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); audioProcID = 0; #endif } } } } return started; } void setCallback (AudioIODeviceCallback* cb) { const ScopedLock sl (callbackLock); callback = cb; } void stop (bool leaveInterruptRunning) { { const ScopedLock sl (callbackLock); callback = nullptr; } if (started && (deviceID != 0) && ! leaveInterruptRunning) { OK (AudioDeviceStop (deviceID, audioIOProc)); #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); #else OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); audioProcID = 0; #endif started = false; { const ScopedLock sl (callbackLock); } // wait until it's definately stopped calling back.. for (int i = 40; --i >= 0;) { Thread::sleep (50); UInt32 running = 0; UInt32 size = sizeof (running); AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyDeviceIsRunning; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &running)); if (running == 0) break; } const ScopedLock sl (callbackLock); } } double getSampleRate() const { return sampleRate; } int getBufferSize() const { return bufferSize; } void audioCallback (const AudioBufferList* inInputData, AudioBufferList* outOutputData) { const ScopedLock sl (callbackLock); if (callback != nullptr) { for (int i = numInputChans; --i >= 0;) { const CallbackDetailsForChannel& info = inputChannelInfo.getReference(i); float* dest = tempInputBuffers [i]; const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; const int stride = info.dataStrideSamples; if (stride != 0) // if this is zero, info is invalid { for (int j = bufferSize; --j >= 0;) { *dest++ = *src; src += stride; } } } callback->audioDeviceIOCallback (const_cast (tempInputBuffers.getData()), numInputChans, tempOutputBuffers, numOutputChans, bufferSize); for (int i = numOutputChans; --i >= 0;) { const CallbackDetailsForChannel& info = outputChannelInfo.getReference(i); const float* src = tempOutputBuffers [i]; float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; const int stride = info.dataStrideSamples; if (stride != 0) // if this is zero, info is invalid { for (int j = bufferSize; --j >= 0;) { *dest = *src++; dest += stride; } } } } else { for (UInt32 i = 0; i < outOutputData->mNumberBuffers; ++i) zeromem (outOutputData->mBuffers[i].mData, outOutputData->mBuffers[i].mDataByteSize); } } // called by callbacks void deviceDetailsChanged() { if (callbacksAllowed) startTimer (100); } void timerCallback() override { JUCE_COREAUDIOLOG ("Device changed"); stopTimer(); const double oldSampleRate = sampleRate; const int oldBufferSize = bufferSize; updateDetailsFromDevice(); if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) owner.restart(); } //============================================================================== CoreAudioIODevice& owner; int inputLatency, outputLatency; int bitDepth; BigInteger activeInputChans, activeOutputChans; StringArray inChanNames, outChanNames; Array sampleRates; Array bufferSizes; AudioIODeviceCallback* callback; #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 AudioDeviceIOProcID audioProcID; #endif private: CriticalSection callbackLock; AudioDeviceID deviceID; bool started; double sampleRate; int bufferSize; HeapBlock audioBuffer; int numInputChans, numOutputChans; bool callbacksAllowed; Array inputChannelInfo, outputChannelInfo; HeapBlock tempInputBuffers, tempOutputBuffers; //============================================================================== static OSStatus audioIOProc (AudioDeviceID /*inDevice*/, const AudioTimeStamp* /*inNow*/, const AudioBufferList* inInputData, const AudioTimeStamp* /*inInputTime*/, AudioBufferList* outOutputData, const AudioTimeStamp* /*inOutputTime*/, void* device) { static_cast (device)->audioCallback (inInputData, outOutputData); return noErr; } static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { CoreAudioInternal* const intern = static_cast (inClientData); switch (pa->mSelector) { case kAudioDevicePropertyBufferSize: case kAudioDevicePropertyBufferFrameSize: case kAudioDevicePropertyNominalSampleRate: case kAudioDevicePropertyStreamFormat: case kAudioDevicePropertyDeviceIsAlive: case kAudioStreamPropertyPhysicalFormat: intern->deviceDetailsChanged(); break; case kAudioDevicePropertyBufferSizeRange: case kAudioDevicePropertyVolumeScalar: case kAudioDevicePropertyMute: case kAudioDevicePropertyPlayThru: case kAudioDevicePropertyDataSource: case kAudioDevicePropertyDeviceIsRunning: break; } return noErr; } //============================================================================== static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock& types) { AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyDataSources; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; UInt32 size = 0; if (deviceID != 0 && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) { types.calloc (size, 1); if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, types) == noErr) return size / (int) sizeof (OSType); } return 0; } bool OK (const OSStatus errorCode) const { if (errorCode == noErr) return true; const String errorMessage ("CoreAudio error: " + String::toHexString ((int) errorCode)); JUCE_COREAUDIOLOG (errorMessage); if (callback != nullptr) callback->audioDeviceError (errorMessage); return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioInternal) }; //============================================================================== class CoreAudioIODevice : public AudioIODevice { public: CoreAudioIODevice (const String& deviceName, AudioDeviceID inputDeviceId, const int inputIndex_, AudioDeviceID outputDeviceId, const int outputIndex_) : AudioIODevice (deviceName, "CoreAudio"), inputIndex (inputIndex_), outputIndex (outputIndex_), isOpen_ (false), isStarted (false) { CoreAudioInternal* device = nullptr; if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) { jassert (inputDeviceId != 0); device = new CoreAudioInternal (*this, inputDeviceId); } else { device = new CoreAudioInternal (*this, outputDeviceId); } internal = device; jassert (device != nullptr); AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); } ~CoreAudioIODevice() { close(); AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); } StringArray getOutputChannelNames() override { return internal->outChanNames; } StringArray getInputChannelNames() override { return internal->inChanNames; } bool isOpen() override { return isOpen_; } Array getAvailableSampleRates() override { return internal->sampleRates; } Array getAvailableBufferSizes() override { return internal->bufferSizes; } double getCurrentSampleRate() override { return internal->getSampleRate(); } int getCurrentBitDepth() override { return internal->bitDepth; } int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); } int getDefaultBufferSize() override { int best = 0; for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i) best = internal->bufferSizes.getUnchecked(i); if (best == 0) best = 512; return best; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) override { isOpen_ = true; if (bufferSizeSamples <= 0) bufferSizeSamples = getDefaultBufferSize(); lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); JUCE_COREAUDIOLOG ("Opened: " << getName()); JUCE_COREAUDIOLOG ("Latencies: " << getInputLatencyInSamples() << ' ' << getOutputLatencyInSamples()); isOpen_ = lastError.isEmpty(); return lastError; } void close() override { isOpen_ = false; internal->stop (false); } void restart() { JUCE_COREAUDIOLOG ("Restarting"); AudioIODeviceCallback* oldCallback = internal->callback; stop(); start (oldCallback); } BigInteger getActiveOutputChannels() const override { return internal->activeOutputChans; } BigInteger getActiveInputChannels() const override { return internal->activeInputChans; } int getOutputLatencyInSamples() override { // this seems like a good guess at getting the latency right - comparing // this with a round-trip measurement, it gets it to within a few millisecs // for the built-in mac soundcard return internal->outputLatency; } int getInputLatencyInSamples() override { return internal->inputLatency; } void start (AudioIODeviceCallback* callback) override { if (! isStarted) { if (callback != nullptr) callback->audioDeviceAboutToStart (this); isStarted = internal->start(); if (isStarted) internal->setCallback (callback); } } void stop() override { if (isStarted) { AudioIODeviceCallback* const lastCallback = internal->callback; isStarted = false; internal->stop (true); if (lastCallback != nullptr) lastCallback->audioDeviceStopped(); } } bool isPlaying() override { if (internal->callback == nullptr) isStarted = false; return isStarted; } String getLastError() override { return lastError; } int inputIndex, outputIndex; private: ScopedPointer internal; bool isOpen_, isStarted; String lastError; static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { switch (pa->mSelector) { case kAudioHardwarePropertyDevices: static_cast (inClientData)->deviceDetailsChanged(); break; case kAudioHardwarePropertyDefaultOutputDevice: case kAudioHardwarePropertyDefaultInputDevice: case kAudioHardwarePropertyDefaultSystemOutputDevice: break; } return noErr; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODevice) }; //============================================================================== class AudioIODeviceCombiner : public AudioIODevice, private Thread { public: AudioIODeviceCombiner (const String& deviceName) : AudioIODevice (deviceName, "CoreAudio"), Thread (deviceName), callback (nullptr), currentSampleRate (0), currentBufferSize (0), active (false) { } ~AudioIODeviceCombiner() { close(); devices.clear(); } void addDevice (AudioIODevice* device, bool useInputs, bool useOutputs) { jassert (device != nullptr); jassert (! isOpen()); jassert (! device->isOpen()); devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); if (currentSampleRate == 0) currentSampleRate = device->getCurrentSampleRate(); if (currentBufferSize == 0) currentBufferSize = device->getCurrentBufferSizeSamples(); } Array getDevices() const { Array devs; for (int i = 0; i < devices.size(); ++i) devs.add (devices.getUnchecked(i)->device); return devs; } StringArray getOutputChannelNames() override { StringArray names; for (int i = 0; i < devices.size(); ++i) names.addArray (devices.getUnchecked(i)->getOutputChannelNames()); names.appendNumbersToDuplicates (false, true); return names; } StringArray getInputChannelNames() override { StringArray names; for (int i = 0; i < devices.size(); ++i) names.addArray (devices.getUnchecked(i)->getInputChannelNames()); names.appendNumbersToDuplicates (false, true); return names; } Array getAvailableSampleRates() override { Array commonRates; for (int i = 0; i < devices.size(); ++i) { Array rates (devices.getUnchecked(i)->device->getAvailableSampleRates()); if (i == 0) commonRates = rates; else commonRates.removeValuesNotIn (rates); } return commonRates; } Array getAvailableBufferSizes() override { Array commonSizes; for (int i = 0; i < devices.size(); ++i) { Array sizes (devices.getUnchecked(i)->device->getAvailableBufferSizes()); if (i == 0) commonSizes = sizes; else commonSizes.removeValuesNotIn (sizes); } return commonSizes; } bool isOpen() override { return active; } bool isPlaying() override { return callback != nullptr; } double getCurrentSampleRate() override { return currentSampleRate; } int getCurrentBufferSizeSamples() override { return currentBufferSize; } int getCurrentBitDepth() override { int depth = 32; for (int i = 0; i < devices.size(); ++i) depth = jmin (depth, devices.getUnchecked(i)->device->getCurrentBitDepth()); return depth; } int getDefaultBufferSize() override { int size = 0; for (int i = 0; i < devices.size(); ++i) size = jmax (size, devices.getUnchecked(i)->device->getDefaultBufferSize()); return size; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSize) override { close(); active = true; if (bufferSize <= 0) bufferSize = getDefaultBufferSize(); if (sampleRate <= 0) { Array rates (getAvailableSampleRates()); for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i) sampleRate = rates.getUnchecked(i); } currentSampleRate = sampleRate; currentBufferSize = bufferSize; const int fifoSize = bufferSize * 3 + 1; int totalInputChanIndex = 0, totalOutputChanIndex = 0; int chanIndex = 0; for (int i = 0; i < devices.size(); ++i) { DeviceWrapper& d = *devices.getUnchecked(i); BigInteger ins (inputChannels >> totalInputChanIndex); BigInteger outs (outputChannels >> totalOutputChanIndex); int numIns = d.getInputChannelNames().size(); int numOuts = d.getOutputChannelNames().size(); totalInputChanIndex += numIns; totalOutputChanIndex += numOuts; String err = d.open (ins, outs, sampleRate, bufferSize, chanIndex, fifoSize); if (err.isNotEmpty()) { close(); lastError = err; return err; } chanIndex += d.numInputChans + d.numOutputChans; } fifos.setSize (chanIndex, fifoSize); fifos.clear(); startThread (9); return String(); } void close() override { stop(); stopThread (10000); fifos.clear(); active = false; for (int i = 0; i < devices.size(); ++i) devices.getUnchecked(i)->close(); } BigInteger getActiveOutputChannels() const override { BigInteger chans; int start = 0; for (int i = 0; i < devices.size(); ++i) { const int numChans = devices.getUnchecked(i)->getOutputChannelNames().size(); if (numChans > 0) { chans |= (devices.getUnchecked(i)->device->getActiveOutputChannels() << start); start += numChans; } } return chans; } BigInteger getActiveInputChannels() const override { BigInteger chans; int start = 0; for (int i = 0; i < devices.size(); ++i) { const int numChans = devices.getUnchecked(i)->getInputChannelNames().size(); if (numChans > 0) { chans |= (devices.getUnchecked(i)->device->getActiveInputChannels() << start); start += numChans; } } return chans; } int getOutputLatencyInSamples() override { int lat = 0; for (int i = 0; i < devices.size(); ++i) lat = jmax (lat, devices.getUnchecked(i)->device->getOutputLatencyInSamples()); return lat + currentBufferSize * 2; } int getInputLatencyInSamples() override { int lat = 0; for (int i = 0; i < devices.size(); ++i) lat = jmax (lat, devices.getUnchecked(i)->device->getInputLatencyInSamples()); return lat + currentBufferSize * 2; } void start (AudioIODeviceCallback* newCallback) override { if (callback != newCallback) { stop(); fifos.clear(); for (int i = 0; i < devices.size(); ++i) devices.getUnchecked(i)->start(); if (newCallback != nullptr) newCallback->audioDeviceAboutToStart (this); const ScopedLock sl (callbackLock); callback = newCallback; } } void stop() override { AudioIODeviceCallback* lastCallback = nullptr; { const ScopedLock sl (callbackLock); std::swap (callback, lastCallback); } for (int i = 0; i < devices.size(); ++i) devices.getUnchecked(i)->device->stop(); if (lastCallback != nullptr) lastCallback->audioDeviceStopped(); } String getLastError() override { return lastError; } private: CriticalSection callbackLock; AudioIODeviceCallback* callback; double currentSampleRate; int currentBufferSize; bool active; String lastError; AudioSampleBuffer fifos; void run() override { const int numSamples = currentBufferSize; AudioSampleBuffer buffer (fifos.getNumChannels(), numSamples); buffer.clear(); Array inputChans; Array outputChans; for (int i = 0; i < devices.size(); ++i) { DeviceWrapper& d = *devices.getUnchecked(i); for (int j = 0; j < d.numInputChans; ++j) inputChans.add (buffer.getReadPointer (d.inputIndex + j)); for (int j = 0; j < d.numOutputChans; ++j) outputChans.add (buffer.getWritePointer (d.outputIndex + j)); } const int numInputChans = inputChans.size(); const int numOutputChans = outputChans.size(); inputChans.add (nullptr); outputChans.add (nullptr); const int blockSizeMs = jmax (1, (int) (1000 * numSamples / currentSampleRate)); jassert (numInputChans + numOutputChans == buffer.getNumChannels()); while (! threadShouldExit()) { readInput (buffer, numSamples, blockSizeMs); bool didCallback = true; { const ScopedLock sl (callbackLock); if (callback != nullptr) callback->audioDeviceIOCallback ((const float**) inputChans.getRawDataPointer(), numInputChans, outputChans.getRawDataPointer(), numOutputChans, numSamples); else didCallback = false; } if (didCallback) { pushOutputData (buffer, numSamples, blockSizeMs); } else { for (int i = 0; i < numOutputChans; ++i) FloatVectorOperations::clear (outputChans[i], numSamples); reset(); } } } void reset() { for (int i = 0; i < devices.size(); ++i) devices.getUnchecked(i)->reset(); } void underrun() { } void readInput (AudioSampleBuffer& buffer, const int numSamples, const int blockSizeMs) { for (int i = 0; i < devices.size(); ++i) { DeviceWrapper& d = *devices.getUnchecked(i); d.done = (d.numInputChans == 0); } for (int tries = 5;;) { bool anyRemaining = false; for (int i = 0; i < devices.size(); ++i) { DeviceWrapper& d = *devices.getUnchecked(i); if (! d.done) { if (d.isInputReady (numSamples)) { d.readInput (buffer, numSamples); d.done = true; } else anyRemaining = true; } } if (! anyRemaining) return; if (--tries == 0) break; wait (blockSizeMs); } for (int j = 0; j < devices.size(); ++j) { DeviceWrapper& d = *devices.getUnchecked(j); if (! d.done) for (int i = 0; i < d.numInputChans; ++i) buffer.clear (d.inputIndex + i, 0, numSamples); } } void pushOutputData (AudioSampleBuffer& buffer, const int numSamples, const int blockSizeMs) { for (int i = 0; i < devices.size(); ++i) { DeviceWrapper& d = *devices.getUnchecked(i); d.done = (d.numOutputChans == 0); } for (int tries = 5;;) { bool anyRemaining = false; for (int i = 0; i < devices.size(); ++i) { DeviceWrapper& d = *devices.getUnchecked(i); if (! d.done) { if (d.isOutputReady (numSamples)) { d.pushOutputData (buffer, numSamples); d.done = true; } else anyRemaining = true; } } if ((! anyRemaining) || --tries == 0) return; wait (blockSizeMs); } } //============================================================================== struct DeviceWrapper : private AudioIODeviceCallback { DeviceWrapper (AudioIODeviceCombiner& cd, AudioIODevice* d, bool useIns, bool useOuts) : owner (cd), device (d), inputIndex (0), outputIndex (0), useInputs (useIns), useOutputs (useOuts), inputFifo (32), outputFifo (32), done (false) { } ~DeviceWrapper() { close(); } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSize, int channelIndex, int fifoSize) { inputFifo.setTotalSize (fifoSize); outputFifo.setTotalSize (fifoSize); inputFifo.reset(); outputFifo.reset(); String err (device->open (useInputs ? inputChannels : BigInteger(), useOutputs ? outputChannels : BigInteger(), sampleRate, bufferSize)); numInputChans = useInputs ? device->getActiveInputChannels().countNumberOfSetBits() : 0; numOutputChans = useOutputs ? device->getActiveOutputChannels().countNumberOfSetBits() : 0; inputIndex = channelIndex; outputIndex = channelIndex + numInputChans; return err; } void close() { device->close(); } void start() { reset(); device->start (this); } void reset() { inputFifo.reset(); outputFifo.reset(); } StringArray getOutputChannelNames() const { return useOutputs ? device->getOutputChannelNames() : StringArray(); } StringArray getInputChannelNames() const { return useInputs ? device->getInputChannelNames() : StringArray(); } bool isInputReady (int numSamples) const noexcept { return numInputChans == 0 || inputFifo.getNumReady() >= numSamples; } void readInput (AudioSampleBuffer& destBuffer, int numSamples) { if (numInputChans == 0) return; int start1, size1, start2, size2; inputFifo.prepareToRead (numSamples, start1, size1, start2, size2); for (int i = 0; i < numInputChans; ++i) { const int index = inputIndex + i; float* const dest = destBuffer.getWritePointer (index); const float* const src = owner.fifos.getReadPointer (index); if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); } inputFifo.finishedRead (size1 + size2); } bool isOutputReady (int numSamples) const noexcept { return numOutputChans == 0 || outputFifo.getFreeSpace() >= numSamples; } void pushOutputData (AudioSampleBuffer& srcBuffer, int numSamples) { if (numOutputChans == 0) return; int start1, size1, start2, size2; outputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); for (int i = 0; i < numOutputChans; ++i) { const int index = outputIndex + i; float* const dest = owner.fifos.getWritePointer (index); const float* const src = srcBuffer.getReadPointer (index); if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); } outputFifo.finishedWrite (size1 + size2); } void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels, float** outputChannelData, int numOutputChannels, int numSamples) override { AudioSampleBuffer& buf = owner.fifos; if (numInputChannels > 0) { int start1, size1, start2, size2; inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); if (size1 + size2 < numSamples) { inputFifo.reset(); inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); } for (int i = 0; i < numInputChannels; ++i) { float* const dest = buf.getWritePointer (inputIndex + i); const float* const src = inputChannelData[i]; if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); } inputFifo.finishedWrite (size1 + size2); if (numSamples > size1 + size2) { for (int i = 0; i < numInputChans; ++i) buf.clear (inputIndex + i, size1 + size2, numSamples - (size1 + size2)); owner.underrun(); } } if (numOutputChannels > 0) { int start1, size1, start2, size2; outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); if (size1 + size2 < numSamples) { Thread::sleep (1); outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); } for (int i = 0; i < numOutputChannels; ++i) { float* const dest = outputChannelData[i]; const float* const src = buf.getReadPointer (outputIndex + i); if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); } outputFifo.finishedRead (size1 + size2); if (numSamples > size1 + size2) { for (int i = 0; i < numOutputChannels; ++i) FloatVectorOperations::clear (outputChannelData[i] + (size1 + size2), numSamples - (size1 + size2)); owner.underrun(); } } owner.notify(); } void audioDeviceAboutToStart (AudioIODevice*) override {} void audioDeviceStopped() override {} void audioDeviceError (const String& errorMessage) override { const ScopedLock sl (owner.callbackLock); if (owner.callback != nullptr) owner.callback->audioDeviceError (errorMessage); } AudioIODeviceCombiner& owner; ScopedPointer device; int inputIndex, numInputChans, outputIndex, numOutputChans; bool useInputs, useOutputs; AbstractFifo inputFifo, outputFifo; bool done; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper) }; OwnedArray devices; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner) }; //============================================================================== class CoreAudioIODeviceType : public AudioIODeviceType { public: CoreAudioIODeviceType() : AudioIODeviceType ("CoreAudio"), hasScanned (false) { AudioObjectPropertyAddress pa; pa.mSelector = kAudioHardwarePropertyDevices; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); } ~CoreAudioIODeviceType() { AudioObjectPropertyAddress pa; pa.mSelector = kAudioHardwarePropertyDevices; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); } //============================================================================== void scanForDevices() { hasScanned = true; inputDeviceNames.clear(); outputDeviceNames.clear(); inputIds.clear(); outputIds.clear(); UInt32 size; AudioObjectPropertyAddress pa; pa.mSelector = kAudioHardwarePropertyDevices; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr) { HeapBlock devs; devs.calloc (size, 1); if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr) { const int num = size / (int) sizeof (AudioDeviceID); for (int i = 0; i < num; ++i) { char name [1024]; size = sizeof (name); pa.mSelector = kAudioDevicePropertyDeviceName; if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr) { const String nameString (String::fromUTF8 (name, (int) strlen (name))); const int numIns = getNumChannels (devs[i], true); const int numOuts = getNumChannels (devs[i], false); if (numIns > 0) { inputDeviceNames.add (nameString); inputIds.add (devs[i]); } if (numOuts > 0) { outputDeviceNames.add (nameString); outputIds.add (devs[i]); } } } } } inputDeviceNames.appendNumbersToDuplicates (false, true); outputDeviceNames.appendNumbersToDuplicates (false, true); } StringArray getDeviceNames (bool wantInputNames) const { jassert (hasScanned); // need to call scanForDevices() before doing this return wantInputNames ? inputDeviceNames : outputDeviceNames; } int getDefaultDeviceIndex (bool forInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this AudioDeviceID deviceID; UInt32 size = sizeof (deviceID); // if they're asking for any input channels at all, use the default input, so we // get the built-in mic rather than the built-in output with no inputs.. AudioObjectPropertyAddress pa; pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr) { if (forInput) { for (int i = inputIds.size(); --i >= 0;) if (inputIds[i] == deviceID) return i; } else { for (int i = outputIds.size(); --i >= 0;) if (outputIds[i] == deviceID) return i; } } return 0; } int getIndexOfDevice (AudioIODevice* device, bool asInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this if (CoreAudioIODevice* const d = dynamic_cast (device)) return asInput ? d->inputIndex : d->outputIndex; if (AudioIODeviceCombiner* const d = dynamic_cast (device)) { const Array devs (d->getDevices()); for (int i = 0; i < devs.size(); ++i) { const int index = getIndexOfDevice (devs.getUnchecked(i), asInput); if (index >= 0) return index; } } return -1; } bool hasSeparateInputsAndOutputs() const { return true; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { jassert (hasScanned); // need to call scanForDevices() before doing this const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); AudioDeviceID inputDeviceID = inputIds [inputIndex]; AudioDeviceID outputDeviceID = outputIds [outputIndex]; if (inputDeviceID == 0 && outputDeviceID == 0) return nullptr; String combinedName (outputDeviceName.isEmpty() ? inputDeviceName : outputDeviceName); if (inputDeviceID == outputDeviceID) return new CoreAudioIODevice (combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex); ScopedPointer in, out; if (inputDeviceID != 0) in = new CoreAudioIODevice (inputDeviceName, inputDeviceID, inputIndex, 0, -1); if (outputDeviceID != 0) out = new CoreAudioIODevice (outputDeviceName, 0, -1, outputDeviceID, outputIndex); if (in == nullptr) return out.release(); if (out == nullptr) return in.release(); ScopedPointer combo (new AudioIODeviceCombiner (combinedName)); combo->addDevice (in.release(), true, false); combo->addDevice (out.release(), false, true); return combo.release(); } //============================================================================== private: StringArray inputDeviceNames, outputDeviceNames; Array inputIds, outputIds; bool hasScanned; static int getNumChannels (AudioDeviceID deviceID, bool input) { int total = 0; UInt32 size; AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyStreamConfiguration; pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) { HeapBlock bufList; bufList.calloc (size, 1); if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr) { const int numStreams = (int) bufList->mNumberBuffers; for (int i = 0; i < numStreams; ++i) { const AudioBuffer& b = bufList->mBuffers[i]; total += b.mNumberChannels; } } } return total; } void audioDeviceListChanged() { scanForDevices(); callDeviceChangeListeners(); } static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) { static_cast (clientData)->audioDeviceListChanged(); return noErr; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType) }; }; //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return new CoreAudioClasses::CoreAudioIODeviceType(); } #undef JUCE_COREAUDIOLOG libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp000066400000000000000000000423271320201440200327400ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_LOG_COREMIDI_ERRORS #define JUCE_LOG_COREMIDI_ERRORS 1 #endif namespace CoreMidiHelpers { static bool checkError (const OSStatus err, const int lineNum) { if (err == noErr) return true; #if JUCE_LOG_COREMIDI_ERRORS Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err)); #endif (void) lineNum; return false; } #undef CHECK_ERROR #define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__) //============================================================================== struct ScopedCFString { ScopedCFString() noexcept : cfString (nullptr) {} ~ScopedCFString() noexcept { if (cfString != nullptr) CFRelease (cfString); } CFStringRef cfString; }; static String getMidiObjectName (MIDIObjectRef entity) { String result; CFStringRef str = nullptr; MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); if (str != nullptr) { result = String::fromCFString (str); CFRelease (str); } return result; } static String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) { String result (getMidiObjectName (endpoint)); MIDIEntityRef entity = 0; // NB: don't attempt to use nullptr for refs - it fails in some types of build. MIDIEndpointGetEntity (endpoint, &entity); if (entity == 0) return result; // probably virtual if (result.isEmpty()) result = getMidiObjectName (entity); // endpoint name is empty - try the entity // now consider the device's name MIDIDeviceRef device = 0; MIDIEntityGetDevice (entity, &device); if (device != 0) { const String deviceName (getMidiObjectName (device)); if (deviceName.isNotEmpty()) { // if an external device has only one entity, throw away // the endpoint name and just use the device name if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2) { result = deviceName; } else if (! result.startsWithIgnoreCase (deviceName)) { // prepend the device name to the entity name result = (deviceName + " " + result).trimEnd(); } } } return result; } static String getConnectedEndpointName (MIDIEndpointRef endpoint) { String result; // Does the endpoint have connections? CFDataRef connections = nullptr; int numConnections = 0; MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); if (connections != nullptr) { numConnections = ((int) CFDataGetLength (connections)) / (int) sizeof (MIDIUniqueID); if (numConnections > 0) { const SInt32* pid = reinterpret_cast (CFDataGetBytePtr (connections)); for (int i = 0; i < numConnections; ++i, ++pid) { MIDIUniqueID uid = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid); MIDIObjectRef connObject; MIDIObjectType connObjectType; OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType); if (err == noErr) { String s; if (connObjectType == kMIDIObjectType_ExternalSource || connObjectType == kMIDIObjectType_ExternalDestination) { // Connected to an external device's endpoint (10.3 and later). s = getEndpointName (static_cast (connObject), true); } else { // Connected to an external device (10.2) (or something else, catch-all) s = getMidiObjectName (connObject); } if (s.isNotEmpty()) { if (result.isNotEmpty()) result += ", "; result += s; } } } } CFRelease (connections); } if (result.isEmpty()) // Here, either the endpoint had no connections, or we failed to obtain names for them. result = getEndpointName (endpoint, false); return result; } static StringArray findDevices (const bool forInput) { const ItemCount num = forInput ? MIDIGetNumberOfSources() : MIDIGetNumberOfDestinations(); StringArray s; for (ItemCount i = 0; i < num; ++i) { MIDIEndpointRef dest = forInput ? MIDIGetSource (i) : MIDIGetDestination (i); String name; if (dest != 0) name = getConnectedEndpointName (dest); if (name.isEmpty()) name = ""; s.add (name); } return s; } static void globalSystemChangeCallback (const MIDINotification*, void*) { // TODO.. Should pass-on this notification.. } static String getGlobalMidiClientName() { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) return app->getApplicationName(); return "JUCE"; } static MIDIClientRef getGlobalMidiClient() { static MIDIClientRef globalMidiClient = 0; if (globalMidiClient == 0) { // Since OSX 10.6, the MIDIClientCreate function will only work // correctly when called from the message thread! jassert (MessageManager::getInstance()->isThisTheMessageThread()); CoreMidiHelpers::ScopedCFString name; name.cfString = getGlobalMidiClientName().toCFString(); CHECK_ERROR (MIDIClientCreate (name.cfString, &globalSystemChangeCallback, nullptr, &globalMidiClient)); } return globalMidiClient; } //============================================================================== class MidiPortAndEndpoint { public: MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) noexcept : port (p), endPoint (ep) { } ~MidiPortAndEndpoint() noexcept { if (port != 0) MIDIPortDispose (port); if (port == 0 && endPoint != 0) // if port == nullptr, it means we created the endpoint, so it's safe to delete it MIDIEndpointDispose (endPoint); } void send (const MIDIPacketList* const packets) noexcept { if (port != 0) MIDISend (port, endPoint, packets); else MIDIReceived (endPoint, packets); } MIDIPortRef port; MIDIEndpointRef endPoint; }; //============================================================================== class MidiPortAndCallback; CriticalSection callbackLock; Array activeCallbacks; class MidiPortAndCallback { public: MidiPortAndCallback (MidiInputCallback& cb) : input (nullptr), active (false), callback (cb), concatenator (2048) { } ~MidiPortAndCallback() { active = false; { const ScopedLock sl (callbackLock); activeCallbacks.removeFirstMatchingValue (this); } if (portAndEndpoint != 0 && portAndEndpoint->port != 0) CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint)); } void handlePackets (const MIDIPacketList* const pktlist) { const double time = Time::getMillisecondCounterHiRes() * 0.001; const ScopedLock sl (callbackLock); if (activeCallbacks.contains (this) && active) { const MIDIPacket* packet = &pktlist->packet[0]; for (unsigned int i = 0; i < pktlist->numPackets; ++i) { concatenator.pushMidiData (packet->data, (int) packet->length, time, input, callback); packet = MIDIPacketNext (packet); } } } MidiInput* input; ScopedPointer portAndEndpoint; volatile bool active; private: MidiInputCallback& callback; MidiDataConcatenator concatenator; }; static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/) { static_cast (readProcRefCon)->handlePackets (pktlist); } } //============================================================================== StringArray MidiOutput::getDevices() { return CoreMidiHelpers::findDevices (false); } int MidiOutput::getDefaultDeviceIndex() { return 0; } MidiOutput* MidiOutput::openDevice (int index) { MidiOutput* mo = nullptr; if (isPositiveAndBelow (index, (int) MIDIGetNumberOfDestinations())) { MIDIEndpointRef endPoint = MIDIGetDestination ((ItemCount) index); CoreMidiHelpers::ScopedCFString pname; if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString))) { MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); MIDIPortRef port; if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port))) { mo = new MidiOutput(); mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint); } } } return mo; } MidiOutput* MidiOutput::createNewDevice (const String& deviceName) { MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); MIDIEndpointRef endPoint; CoreMidiHelpers::ScopedCFString name; name.cfString = deviceName.toCFString(); if (client != 0 && CHECK_ERROR (MIDISourceCreate (client, name.cfString, &endPoint))) { MidiOutput* mo = new MidiOutput(); mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint); return mo; } return nullptr; } MidiOutput::~MidiOutput() { stopBackgroundThread(); delete static_cast (internal); } void MidiOutput::sendMessageNow (const MidiMessage& message) { #if JUCE_IOS const MIDITimeStamp timeStamp = mach_absolute_time(); #else const MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); #endif HeapBlock allocatedPackets; MIDIPacketList stackPacket; MIDIPacketList* packetToSend = &stackPacket; const size_t dataSize = (size_t) message.getRawDataSize(); if (message.isSysEx()) { const int maxPacketSize = 256; int pos = 0, bytesLeft = (int) dataSize; const int numPackets = (bytesLeft + maxPacketSize - 1) / maxPacketSize; allocatedPackets.malloc ((size_t) (32 * (size_t) numPackets + dataSize), 1); packetToSend = allocatedPackets; packetToSend->numPackets = (UInt32) numPackets; MIDIPacket* p = packetToSend->packet; for (int i = 0; i < numPackets; ++i) { p->timeStamp = timeStamp; p->length = (UInt16) jmin (maxPacketSize, bytesLeft); memcpy (p->data, message.getRawData() + pos, p->length); pos += p->length; bytesLeft -= p->length; p = MIDIPacketNext (p); } } else if (dataSize < 65536) // max packet size { const size_t stackCapacity = sizeof (stackPacket.packet->data); if (dataSize > stackCapacity) { allocatedPackets.malloc ((sizeof (MIDIPacketList) - stackCapacity) + dataSize, 1); packetToSend = allocatedPackets; } packetToSend->numPackets = 1; MIDIPacket& p = *(packetToSend->packet); p.timeStamp = timeStamp; p.length = (UInt16) dataSize; memcpy (p.data, message.getRawData(), dataSize); } else { jassertfalse; // packet too large to send! return; } static_cast (internal)->send (packetToSend); } //============================================================================== StringArray MidiInput::getDevices() { return CoreMidiHelpers::findDevices (true); } int MidiInput::getDefaultDeviceIndex() { return 0; } MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { jassert (callback != nullptr); using namespace CoreMidiHelpers; MidiInput* newInput = nullptr; if (isPositiveAndBelow (index, (int) MIDIGetNumberOfSources())) { if (MIDIEndpointRef endPoint = MIDIGetSource ((ItemCount) index)) { ScopedCFString name; if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name.cfString))) { if (MIDIClientRef client = getGlobalMidiClient()) { MIDIPortRef port; ScopedPointer mpc (new MidiPortAndCallback (*callback)); if (CHECK_ERROR (MIDIInputPortCreate (client, name.cfString, midiInputProc, mpc, &port))) { if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, nullptr))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint); newInput = new MidiInput (getDevices() [index]); mpc->input = newInput; newInput->internal = mpc; const ScopedLock sl (callbackLock); activeCallbacks.add (mpc.release()); } else { CHECK_ERROR (MIDIPortDispose (port)); } } } } } } return newInput; } MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) { jassert (callback != nullptr); using namespace CoreMidiHelpers; MidiInput* mi = nullptr; if (MIDIClientRef client = getGlobalMidiClient()) { ScopedPointer mpc (new MidiPortAndCallback (*callback)); mpc->active = false; MIDIEndpointRef endPoint; ScopedCFString name; name.cfString = deviceName.toCFString(); if (CHECK_ERROR (MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc, &endPoint))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint); mi = new MidiInput (deviceName); mpc->input = mi; mi->internal = mpc; const ScopedLock sl (callbackLock); activeCallbacks.add (mpc.release()); } } return mi; } MidiInput::MidiInput (const String& nm) : name (nm) { } MidiInput::~MidiInput() { delete static_cast (internal); } void MidiInput::start() { const ScopedLock sl (CoreMidiHelpers::callbackLock); static_cast (internal)->active = true; } void MidiInput::stop() { const ScopedLock sl (CoreMidiHelpers::callbackLock); static_cast (internal)->active = false; } #undef CHECK_ERROR libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp000066400000000000000000001541401320201440200321770ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #undef WINDOWS /* The ASIO SDK *should* declare its callback functions as being __cdecl, but different versions seem to be pretty random about whether or not they do this. If you hit an error using these functions it'll be because you're trying to build using __stdcall, in which case you'd need to either get hold of an ASIO SDK which correctly specifies __cdecl, or add the __cdecl keyword to its functions yourself. */ #define JUCE_ASIOCALLBACK __cdecl //============================================================================== namespace ASIODebugging { #if JUCE_ASIO_DEBUGGING #define JUCE_ASIO_LOG(msg) ASIODebugging::logMessage (msg) #define JUCE_ASIO_LOG_ERROR(msg, errNum) ASIODebugging::logError ((msg), (errNum)) static void logMessage (String message) { message = "ASIO: " + message; DBG (message); Logger::writeToLog (message); } static void logError (const String& context, long error) { const char* err = "Unknown error"; switch (error) { case ASE_OK: return; case ASE_NotPresent: err = "Not Present"; break; case ASE_HWMalfunction: err = "Hardware Malfunction"; break; case ASE_InvalidParameter: err = "Invalid Parameter"; break; case ASE_InvalidMode: err = "Invalid Mode"; break; case ASE_SPNotAdvancing: err = "Sample position not advancing"; break; case ASE_NoClock: err = "No Clock"; break; case ASE_NoMemory: err = "Out of memory"; break; default: break; } logMessage ("error: " + context + " - " + err); } #else static void dummyLog() {} #define JUCE_ASIO_LOG(msg) ASIODebugging::dummyLog() #define JUCE_ASIO_LOG_ERROR(msg, errNum) (void) errNum; ASIODebugging::dummyLog() #endif } //============================================================================== struct ASIOSampleFormat { ASIOSampleFormat() noexcept {} ASIOSampleFormat (const long type) noexcept : bitDepth (24), littleEndian (true), formatIsFloat (false), byteStride (4) { switch (type) { case ASIOSTInt16MSB: byteStride = 2; littleEndian = false; bitDepth = 16; break; case ASIOSTInt24MSB: byteStride = 3; littleEndian = false; break; case ASIOSTInt32MSB: bitDepth = 32; littleEndian = false; break; case ASIOSTFloat32MSB: bitDepth = 32; littleEndian = false; formatIsFloat = true; break; case ASIOSTFloat64MSB: bitDepth = 64; byteStride = 8; littleEndian = false; break; case ASIOSTInt32MSB16: bitDepth = 16; littleEndian = false; break; case ASIOSTInt32MSB18: littleEndian = false; break; case ASIOSTInt32MSB20: littleEndian = false; break; case ASIOSTInt32MSB24: littleEndian = false; break; case ASIOSTInt16LSB: byteStride = 2; bitDepth = 16; break; case ASIOSTInt24LSB: byteStride = 3; break; case ASIOSTInt32LSB: bitDepth = 32; break; case ASIOSTFloat32LSB: bitDepth = 32; formatIsFloat = true; break; case ASIOSTFloat64LSB: bitDepth = 64; byteStride = 8; break; case ASIOSTInt32LSB16: bitDepth = 16; break; case ASIOSTInt32LSB18: break; // (unhandled) case ASIOSTInt32LSB20: break; // (unhandled) case ASIOSTInt32LSB24: break; case ASIOSTDSDInt8LSB1: break; // (unhandled) case ASIOSTDSDInt8MSB1: break; // (unhandled) case ASIOSTDSDInt8NER8: break; // (unhandled) default: jassertfalse; // (not a valid format code..) break; } } void convertToFloat (const void* const src, float* const dst, const int samps) const noexcept { if (formatIsFloat) { memcpy (dst, src, samps * sizeof (float)); } else { switch (bitDepth) { case 16: convertInt16ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; case 24: convertInt24ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; case 32: convertInt32ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; default: jassertfalse; break; } } } void convertFromFloat (const float* const src, void* const dst, const int samps) const noexcept { if (formatIsFloat) { memcpy (dst, src, samps * sizeof (float)); } else { switch (bitDepth) { case 16: convertFloatToInt16 (src, static_cast (dst), byteStride, samps, littleEndian); break; case 24: convertFloatToInt24 (src, static_cast (dst), byteStride, samps, littleEndian); break; case 32: convertFloatToInt32 (src, static_cast (dst), byteStride, samps, littleEndian); break; default: jassertfalse; break; } } } void clear (void* dst, const int numSamps) noexcept { if (dst != nullptr) zeromem (dst, numSamps * byteStride); } int bitDepth, byteStride; bool formatIsFloat, littleEndian; private: static void convertInt16ToFloat (const char* src, float* dest, const int srcStrideBytes, int numSamples, const bool littleEndian) noexcept { const double g = 1.0 / 32768.0; if (littleEndian) { while (--numSamples >= 0) { *dest++ = (float) (g * (short) ByteOrder::littleEndianShort (src)); src += srcStrideBytes; } } else { while (--numSamples >= 0) { *dest++ = (float) (g * (short) ByteOrder::bigEndianShort (src)); src += srcStrideBytes; } } } static void convertFloatToInt16 (const float* src, char* dest, const int dstStrideBytes, int numSamples, const bool littleEndian) noexcept { const double maxVal = (double) 0x7fff; if (littleEndian) { while (--numSamples >= 0) { *(uint16*) dest = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } else { while (--numSamples >= 0) { *(uint16*) dest = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } } static void convertInt24ToFloat (const char* src, float* dest, const int srcStrideBytes, int numSamples, const bool littleEndian) noexcept { const double g = 1.0 / 0x7fffff; if (littleEndian) { while (--numSamples >= 0) { *dest++ = (float) (g * ByteOrder::littleEndian24Bit (src)); src += srcStrideBytes; } } else { while (--numSamples >= 0) { *dest++ = (float) (g * ByteOrder::bigEndian24Bit (src)); src += srcStrideBytes; } } } static void convertFloatToInt24 (const float* src, char* dest, const int dstStrideBytes, int numSamples, const bool littleEndian) noexcept { const double maxVal = (double) 0x7fffff; if (littleEndian) { while (--numSamples >= 0) { ByteOrder::littleEndian24BitToChars ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); dest += dstStrideBytes; } } else { while (--numSamples >= 0) { ByteOrder::bigEndian24BitToChars ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); dest += dstStrideBytes; } } } static void convertInt32ToFloat (const char* src, float* dest, const int srcStrideBytes, int numSamples, const bool littleEndian) noexcept { const double g = 1.0 / 0x7fffffff; if (littleEndian) { while (--numSamples >= 0) { *dest++ = (float) (g * (int) ByteOrder::littleEndianInt (src)); src += srcStrideBytes; } } else { while (--numSamples >= 0) { *dest++ = (float) (g * (int) ByteOrder::bigEndianInt (src)); src += srcStrideBytes; } } } static void convertFloatToInt32 (const float* src, char* dest, const int dstStrideBytes, int numSamples, const bool littleEndian) noexcept { const double maxVal = (double) 0x7fffffff; if (littleEndian) { while (--numSamples >= 0) { *(uint32*) dest = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } else { while (--numSamples >= 0) { *(uint32*) dest = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } } }; //============================================================================== class ASIOAudioIODevice; static ASIOAudioIODevice* volatile currentASIODev[16] = { 0 }; extern HWND juce_messageWindowHandle; class ASIOAudioIODeviceType; static void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType*); //============================================================================== class ASIOAudioIODevice : public AudioIODevice, private Timer { public: ASIOAudioIODevice (ASIOAudioIODeviceType* ownerType, const String& devName, const CLSID clsID, const int slotNumber) : AudioIODevice (devName, "ASIO"), owner (ownerType), asioObject (nullptr), classId (clsID), inputLatency (0), outputLatency (0), minSize (0), maxSize (0), preferredSize (0), granularity (0), numClockSources (0), currentBlockSizeSamples (0), currentBitDepth (16), currentSampleRate (0), currentCallback (nullptr), bufferIndex (0), numActiveInputChans (0), numActiveOutputChans (0), deviceIsOpen (false), isStarted (false), buffersCreated (false), calledback (false), littleEndian (false), postOutput (true), needToReset (false), insideControlPanelModalLoop (false), shouldUsePreferredSize (false) { name = devName; inBuffers.calloc (4); outBuffers.calloc (4); jassert (currentASIODev [slotNumber] == nullptr); currentASIODev [slotNumber] = this; openDevice(); } ~ASIOAudioIODevice() { for (int i = 0; i < numElementsInArray (currentASIODev); ++i) if (currentASIODev[i] == this) currentASIODev[i] = nullptr; close(); JUCE_ASIO_LOG ("closed"); removeCurrentDriver(); } void updateSampleRates() { // find a list of sample rates.. const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; Array newRates; if (asioObject != nullptr) { for (int index = 0; index < numElementsInArray (possibleSampleRates); ++index) if (asioObject->canSampleRate ((double) possibleSampleRates[index]) == 0) newRates.add ((double) possibleSampleRates[index]); } if (newRates.size() == 0) { double cr = getSampleRate(); JUCE_ASIO_LOG ("No sample rates supported - current rate: " + String ((int) cr)); if (cr > 0) newRates.add ((int) cr); } if (sampleRates != newRates) { sampleRates.swapWith (newRates); #if JUCE_ASIO_DEBUGGING StringArray s; for (int i = 0; i < sampleRates.size(); ++i) s.add (String (sampleRates.getUnchecked(i))); JUCE_ASIO_LOG ("Rates: " + s.joinIntoString (" ")); #endif } } StringArray getOutputChannelNames() override { return outputChannelNames; } StringArray getInputChannelNames() override { return inputChannelNames; } Array getAvailableSampleRates() override { return sampleRates; } Array getAvailableBufferSizes() override { return bufferSizes; } int getDefaultBufferSize() override { return preferredSize; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sr, int bufferSizeSamples) override { if (isOpen()) close(); jassert (currentCallback == nullptr); if (bufferSizeSamples < 8 || bufferSizeSamples > 16384) shouldUsePreferredSize = true; if (asioObject == nullptr) { const String openingError (openDevice()); if (asioObject == nullptr) return openingError; } isStarted = false; bufferIndex = -1; long err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); jassert (err == ASE_OK); bufferSizeSamples = readBufferSizes (bufferSizeSamples); double sampleRate = sr; currentSampleRate = sampleRate; currentBlockSizeSamples = bufferSizeSamples; currentChansOut.clear(); currentChansIn.clear(); updateSampleRates(); if (sampleRate == 0 || (sampleRates.size() > 0 && ! sampleRates.contains (sampleRate))) sampleRate = sampleRates[0]; jassert (sampleRate != 0); if (sampleRate == 0) sampleRate = 44100.0; updateClockSources(); currentSampleRate = getSampleRate(); error.clear(); buffersCreated = false; setSampleRate (sampleRate); // (need to get this again in case a sample rate change affected the channel count) err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); jassert (err == ASE_OK); inBuffers.calloc (totalNumInputChans + 8); outBuffers.calloc (totalNumOutputChans + 8); if (needToReset) { JUCE_ASIO_LOG (" Resetting"); removeCurrentDriver(); loadDriver(); const String error (initDriver()); if (error.isNotEmpty()) JUCE_ASIO_LOG ("ASIOInit: " + error); needToReset = false; } const int totalBuffers = resetBuffers (inputChannels, outputChannels); setCallbackFunctions(); JUCE_ASIO_LOG ("disposing buffers"); err = asioObject->disposeBuffers(); JUCE_ASIO_LOG ("creating buffers: " + String (totalBuffers) + ", " + String (currentBlockSizeSamples)); err = asioObject->createBuffers (bufferInfos, totalBuffers, currentBlockSizeSamples, &callbacks); if (err != ASE_OK) { currentBlockSizeSamples = preferredSize; JUCE_ASIO_LOG_ERROR ("create buffers 2", err); asioObject->disposeBuffers(); err = asioObject->createBuffers (bufferInfos, totalBuffers, currentBlockSizeSamples, &callbacks); } if (err == ASE_OK) { buffersCreated = true; tempBuffer.calloc (totalBuffers * currentBlockSizeSamples + 32); int n = 0; Array types; currentBitDepth = 16; for (int i = 0; i < (int) totalNumInputChans; ++i) { if (inputChannels[i]) { inBuffers[n] = tempBuffer + (currentBlockSizeSamples * n); ASIOChannelInfo channelInfo = { 0 }; channelInfo.channel = i; channelInfo.isInput = 1; asioObject->getChannelInfo (&channelInfo); types.addIfNotAlreadyThere (channelInfo.type); inputFormat[n] = ASIOSampleFormat (channelInfo.type); currentBitDepth = jmax (currentBitDepth, inputFormat[n].bitDepth); ++n; } } jassert (numActiveInputChans == n); n = 0; for (int i = 0; i < (int) totalNumOutputChans; ++i) { if (outputChannels[i]) { outBuffers[n] = tempBuffer + (currentBlockSizeSamples * (numActiveInputChans + n)); ASIOChannelInfo channelInfo = { 0 }; channelInfo.channel = i; channelInfo.isInput = 0; asioObject->getChannelInfo (&channelInfo); types.addIfNotAlreadyThere (channelInfo.type); outputFormat[n] = ASIOSampleFormat (channelInfo.type); currentBitDepth = jmax (currentBitDepth, outputFormat[n].bitDepth); ++n; } } jassert (numActiveOutputChans == n); for (int i = types.size(); --i >= 0;) JUCE_ASIO_LOG ("channel format: " + String (types[i])); jassert (n <= totalBuffers); for (int i = 0; i < numActiveOutputChans; ++i) { outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[0], currentBlockSizeSamples); outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[1], currentBlockSizeSamples); } readLatencies(); asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); deviceIsOpen = true; JUCE_ASIO_LOG ("starting"); calledback = false; err = asioObject->start(); if (err != 0) { deviceIsOpen = false; JUCE_ASIO_LOG ("stop on failure"); Thread::sleep (10); asioObject->stop(); error = "Can't start device"; Thread::sleep (10); } else { int count = 300; while (--count > 0 && ! calledback) Thread::sleep (10); isStarted = true; if (! calledback) { error = "Device didn't start correctly"; JUCE_ASIO_LOG ("no callbacks - stopping.."); asioObject->stop(); } } } else { error = "Can't create i/o buffers"; } if (error.isNotEmpty()) { JUCE_ASIO_LOG_ERROR (error, err); disposeBuffers(); Thread::sleep (20); isStarted = false; deviceIsOpen = false; const String errorCopy (error); close(); // (this resets the error string) error = errorCopy; } needToReset = false; return error; } void close() override { error.clear(); stopTimer(); stop(); if (asioObject != nullptr && deviceIsOpen) { const ScopedLock sl (callbackLock); deviceIsOpen = false; isStarted = false; needToReset = false; JUCE_ASIO_LOG ("stopping"); if (asioObject != nullptr) { Thread::sleep (20); asioObject->stop(); Thread::sleep (10); disposeBuffers(); } Thread::sleep (10); } } bool isOpen() override { return deviceIsOpen || insideControlPanelModalLoop; } bool isPlaying() override { return asioObject != nullptr && currentCallback != nullptr; } int getCurrentBufferSizeSamples() override { return currentBlockSizeSamples; } double getCurrentSampleRate() override { return currentSampleRate; } int getCurrentBitDepth() override { return currentBitDepth; } BigInteger getActiveOutputChannels() const override { return currentChansOut; } BigInteger getActiveInputChannels() const override { return currentChansIn; } int getOutputLatencyInSamples() override { return outputLatency + currentBlockSizeSamples / 4; } int getInputLatencyInSamples() override { return inputLatency + currentBlockSizeSamples / 4; } void start (AudioIODeviceCallback* callback) override { if (callback != nullptr) { callback->audioDeviceAboutToStart (this); const ScopedLock sl (callbackLock); currentCallback = callback; } } void stop() override { AudioIODeviceCallback* const lastCallback = currentCallback; { const ScopedLock sl (callbackLock); currentCallback = nullptr; } if (lastCallback != nullptr) lastCallback->audioDeviceStopped(); } String getLastError() { return error; } bool hasControlPanel() const { return true; } bool showControlPanel() { JUCE_ASIO_LOG ("showing control panel"); bool done = false; JUCE_TRY { // are there are devices that need to be closed before showing their control panel? // close(); insideControlPanelModalLoop = true; const uint32 started = Time::getMillisecondCounter(); if (asioObject != nullptr) { asioObject->controlPanel(); const int spent = (int) Time::getMillisecondCounter() - (int) started; JUCE_ASIO_LOG ("spent: " + String (spent)); if (spent > 300) { shouldUsePreferredSize = true; done = true; } } } JUCE_CATCH_ALL insideControlPanelModalLoop = false; return done; } void resetRequest() noexcept { startTimer (500); } void timerCallback() override { if (! insideControlPanelModalLoop) { stopTimer(); JUCE_ASIO_LOG ("restart request!"); AudioIODeviceCallback* const oldCallback = currentCallback; close(); needToReset = true; open (BigInteger (currentChansIn), BigInteger (currentChansOut), currentSampleRate, currentBlockSizeSamples); reloadChannelNames(); if (oldCallback != nullptr) start (oldCallback); sendASIODeviceChangeToListeners (owner); } else { startTimer (100); } } private: //============================================================================== WeakReference owner; IASIO* volatile asioObject; ASIOCallbacks callbacks; CLSID classId; String error; long totalNumInputChans, totalNumOutputChans; StringArray inputChannelNames, outputChannelNames; Array sampleRates; Array bufferSizes; long inputLatency, outputLatency; long minSize, maxSize, preferredSize, granularity; ASIOClockSource clocks[32]; int numClockSources; int volatile currentBlockSizeSamples; int volatile currentBitDepth; double volatile currentSampleRate; BigInteger currentChansOut, currentChansIn; AudioIODeviceCallback* volatile currentCallback; CriticalSection callbackLock; HeapBlock bufferInfos; HeapBlock inBuffers, outBuffers; HeapBlock inputFormat, outputFormat; WaitableEvent event1; HeapBlock tempBuffer; int volatile bufferIndex, numActiveInputChans, numActiveOutputChans; bool deviceIsOpen, isStarted, buffersCreated; bool volatile calledback; bool volatile littleEndian, postOutput, needToReset; bool volatile insideControlPanelModalLoop; bool volatile shouldUsePreferredSize; //============================================================================== static String convertASIOString (char* const text, int length) { if (CharPointer_UTF8::isValidString (text, length)) return String::fromUTF8 (text, length); WCHAR wideVersion [64] = { 0 }; MultiByteToWideChar (CP_ACP, 0, text, length, wideVersion, numElementsInArray (wideVersion)); return wideVersion; } String getChannelName (int index, bool isInput) const { ASIOChannelInfo channelInfo = { 0 }; channelInfo.channel = index; channelInfo.isInput = isInput ? 1 : 0; asioObject->getChannelInfo (&channelInfo); return convertASIOString (channelInfo.name, sizeof (channelInfo.name)); } void reloadChannelNames() { if (asioObject != nullptr && asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans) == ASE_OK) { inputChannelNames.clear(); outputChannelNames.clear(); for (int i = 0; i < totalNumInputChans; ++i) inputChannelNames.add (getChannelName (i, true)); for (int i = 0; i < totalNumOutputChans; ++i) outputChannelNames.add (getChannelName (i, false)); outputChannelNames.trim(); inputChannelNames.trim(); outputChannelNames.appendNumbersToDuplicates (false, true); inputChannelNames.appendNumbersToDuplicates (false, true); } } int readBufferSizes (int bufferSizeSamples) { minSize = 0; maxSize = 0; granularity = 0; long newPreferredSize = 0; if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == ASE_OK) { if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize) shouldUsePreferredSize = true; if (bufferSizeSamples < minSize || bufferSizeSamples > maxSize) shouldUsePreferredSize = true; preferredSize = newPreferredSize; } // unfortunate workaround for certain drivers which crash if you make // dynamic changes to the buffer size... shouldUsePreferredSize = shouldUsePreferredSize || getName().containsIgnoreCase ("Digidesign"); if (shouldUsePreferredSize) { JUCE_ASIO_LOG ("Using preferred size for buffer.."); long err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); if (err == ASE_OK) { bufferSizeSamples = (int) preferredSize; } else { bufferSizeSamples = 1024; JUCE_ASIO_LOG_ERROR ("getBufferSize1", err); } shouldUsePreferredSize = false; } return bufferSizeSamples; } int resetBuffers (const BigInteger& inputChannels, const BigInteger& outputChannels) { numActiveInputChans = 0; numActiveOutputChans = 0; ASIOBufferInfo* info = bufferInfos; for (int i = 0; i < totalNumInputChans; ++i) { if (inputChannels[i]) { currentChansIn.setBit (i); info->isInput = 1; info->channelNum = i; info->buffers[0] = info->buffers[1] = nullptr; ++info; ++numActiveInputChans; } } for (int i = 0; i < totalNumOutputChans; ++i) { if (outputChannels[i]) { currentChansOut.setBit (i); info->isInput = 0; info->channelNum = i; info->buffers[0] = info->buffers[1] = nullptr; ++info; ++numActiveOutputChans; } } return numActiveInputChans + numActiveOutputChans; } void addBufferSizes (long minSize, long maxSize, long preferredSize, long granularity) { // find a list of buffer sizes.. JUCE_ASIO_LOG (String ((int) minSize) + "->" + String ((int) maxSize) + ", " + String ((int) preferredSize) + ", " + String ((int) granularity)); if (granularity >= 0) { granularity = jmax (16, (int) granularity); for (int i = jmax ((int) (minSize + 15) & ~15, (int) granularity); i < jmin (6400, (int) maxSize); i += granularity) bufferSizes.addIfNotAlreadyThere (granularity * (i / granularity)); } else if (granularity < 0) { for (int i = 0; i < 18; ++i) { const int s = (1 << i); if (s >= minSize && s <= maxSize) bufferSizes.add (s); } } bufferSizes.addIfNotAlreadyThere (preferredSize); DefaultElementComparator comparator; bufferSizes.sort (comparator); } double getSampleRate() const { double cr = 0; long err = asioObject->getSampleRate (&cr); JUCE_ASIO_LOG_ERROR ("getSampleRate", err); return cr; } void setSampleRate (double newRate) { if (currentSampleRate != newRate) { JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (newRate)); long err = asioObject->setSampleRate (newRate); if (err == ASE_NoClock && numClockSources > 0) { JUCE_ASIO_LOG ("trying to set a clock source.."); Thread::sleep (10); err = asioObject->setClockSource (clocks[0].index); JUCE_ASIO_LOG_ERROR ("setClockSource2", err); Thread::sleep (10); err = asioObject->setSampleRate (newRate); } if (err == 0) currentSampleRate = newRate; // on fail, ignore the attempt to change rate, and run with the current one.. } } void updateClockSources() { zeromem (clocks, sizeof (clocks)); long numSources = numElementsInArray (clocks); asioObject->getClockSources (clocks, &numSources); numClockSources = (int) numSources; bool isSourceSet = false; // careful not to remove this loop because it does more than just logging! for (int i = 0; i < numClockSources; ++i) { String s ("clock: "); s += clocks[i].name; if (clocks[i].isCurrentSource) { isSourceSet = true; s << " (cur)"; } JUCE_ASIO_LOG (s); } if (numClockSources > 1 && ! isSourceSet) { JUCE_ASIO_LOG ("setting clock source"); long err = asioObject->setClockSource (clocks[0].index); JUCE_ASIO_LOG_ERROR ("setClockSource1", err); Thread::sleep (20); } else { if (numClockSources == 0) JUCE_ASIO_LOG ("no clock sources!"); } } void readLatencies() { inputLatency = outputLatency = 0; if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) JUCE_ASIO_LOG ("getLatencies() failed"); else JUCE_ASIO_LOG ("Latencies: in = " + String ((int) inputLatency) + ", out = " + String ((int) outputLatency)); } void createDummyBuffers (long preferredSize) { numActiveInputChans = 0; numActiveOutputChans = 0; ASIOBufferInfo* info = bufferInfos; int numChans = 0; for (int i = 0; i < jmin (2, (int) totalNumInputChans); ++i) { info->isInput = 1; info->channelNum = i; info->buffers[0] = info->buffers[1] = nullptr; ++info; ++numChans; } const int outputBufferIndex = numChans; for (int i = 0; i < jmin (2, (int) totalNumOutputChans); ++i) { info->isInput = 0; info->channelNum = i; info->buffers[0] = info->buffers[1] = nullptr; ++info; ++numChans; } setCallbackFunctions(); JUCE_ASIO_LOG ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); if (preferredSize > 0) { long err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks); JUCE_ASIO_LOG_ERROR ("dummy buffers", err); } long newInps = 0, newOuts = 0; asioObject->getChannels (&newInps, &newOuts); if (totalNumInputChans != newInps || totalNumOutputChans != newOuts) { totalNumInputChans = newInps; totalNumOutputChans = newOuts; JUCE_ASIO_LOG (String ((int) totalNumInputChans) + " in; " + String ((int) totalNumOutputChans) + " out"); } updateSampleRates(); reloadChannelNames(); for (int i = 0; i < totalNumOutputChans; ++i) { ASIOChannelInfo channelInfo = { 0 }; channelInfo.channel = i; channelInfo.isInput = 0; asioObject->getChannelInfo (&channelInfo); outputFormat[i] = ASIOSampleFormat (channelInfo.type); if (i < 2) { // clear the channels that are used with the dummy stuff outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredSize); outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredSize); } } } void removeCurrentDriver() { if (asioObject != nullptr) { asioObject->Release(); asioObject = nullptr; } } bool loadDriver() { removeCurrentDriver(); bool crashed = false; bool ok = tryCreatingDriver (crashed); if (crashed) JUCE_ASIO_LOG ("** Driver crashed while being opened"); return ok; } bool tryCreatingDriver (bool& crashed) { #if ! JUCE_MINGW __try #endif { return CoCreateInstance (classId, 0, CLSCTX_INPROC_SERVER, classId, (void**) &asioObject) == S_OK; } #if ! JUCE_MINGW __except (EXCEPTION_EXECUTE_HANDLER) { crashed = true; } return false; #endif } String getLastDriverError() const { jassert (asioObject != nullptr); char buffer [512] = { 0 }; asioObject->getErrorMessage (buffer); return String (buffer, sizeof (buffer) - 1); } String initDriver() { if (asioObject == nullptr) return "No Driver"; const bool initOk = !! asioObject->init (juce_messageWindowHandle); String driverError; // Get error message if init() failed, or if it's a buggy Denon driver, // which returns true from init() even when it fails. if ((! initOk) || getName().containsIgnoreCase ("denon dj")) driverError = getLastDriverError(); if ((! initOk) && driverError.isEmpty()) driverError = "Driver failed to initialise"; if (driverError.isEmpty()) { char buffer [512]; asioObject->getDriverName (buffer); // just in case any flimsy drivers expect this to be called.. } return driverError; } String openDevice() { // open the device and get its info.. JUCE_ASIO_LOG ("opening device: " + getName()); needToReset = false; outputChannelNames.clear(); inputChannelNames.clear(); bufferSizes.clear(); sampleRates.clear(); deviceIsOpen = false; totalNumInputChans = 0; totalNumOutputChans = 0; numActiveInputChans = 0; numActiveOutputChans = 0; currentCallback = nullptr; error.clear(); if (getName().isEmpty()) return error; long err = 0; if (loadDriver()) { if ((error = initDriver()).isEmpty()) { numActiveInputChans = 0; numActiveOutputChans = 0; totalNumInputChans = 0; totalNumOutputChans = 0; if (asioObject != nullptr && (err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans)) == 0) { JUCE_ASIO_LOG (String ((int) totalNumInputChans) + " in, " + String ((int) totalNumOutputChans) + " out"); const int chansToAllocate = totalNumInputChans + totalNumOutputChans + 4; bufferInfos.calloc (chansToAllocate); inBuffers.calloc (chansToAllocate); outBuffers.calloc (chansToAllocate); inputFormat.calloc (chansToAllocate); outputFormat.calloc (chansToAllocate); if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == 0) { addBufferSizes (minSize, maxSize, preferredSize, granularity); double currentRate = getSampleRate(); if (currentRate < 1.0 || currentRate > 192001.0) { JUCE_ASIO_LOG ("setting default sample rate"); err = asioObject->setSampleRate (44100.0); JUCE_ASIO_LOG_ERROR ("setting sample rate", err); currentRate = getSampleRate(); } currentSampleRate = currentRate; postOutput = (asioObject->outputReady() == 0); if (postOutput) JUCE_ASIO_LOG ("outputReady true"); updateSampleRates(); readLatencies(); // ..doing these steps because cubase does so at this stage createDummyBuffers (preferredSize); // in initialisation, and some devices fail if we don't. readLatencies(); // start and stop because cubase does it.. err = asioObject->start(); // ignore an error here, as it might start later after setting other stuff up JUCE_ASIO_LOG_ERROR ("start", err); Thread::sleep (80); asioObject->stop(); } else { error = "Can't detect buffer sizes"; } } else { error = "Can't detect asio channels"; } } } else { error = "No such device"; } if (error.isNotEmpty()) { JUCE_ASIO_LOG_ERROR (error, err); disposeBuffers(); removeCurrentDriver(); } else { JUCE_ASIO_LOG ("device open"); } deviceIsOpen = false; needToReset = false; stopTimer(); return error; } void disposeBuffers() { if (asioObject != nullptr && buffersCreated) { buffersCreated = false; asioObject->disposeBuffers(); } } //============================================================================== void JUCE_ASIOCALLBACK callback (const long index) { if (isStarted) { bufferIndex = index; processBuffer(); } else { if (postOutput && (asioObject != nullptr)) asioObject->outputReady(); } calledback = true; } void processBuffer() { const ASIOBufferInfo* const infos = bufferInfos; const int bi = bufferIndex; const ScopedLock sl (callbackLock); if (bi >= 0) { const int samps = currentBlockSizeSamples; if (currentCallback != nullptr) { for (int i = 0; i < numActiveInputChans; ++i) { jassert (inBuffers[i] != nullptr); inputFormat[i].convertToFloat (infos[i].buffers[bi], inBuffers[i], samps); } currentCallback->audioDeviceIOCallback (const_cast (inBuffers.getData()), numActiveInputChans, outBuffers, numActiveOutputChans, samps); for (int i = 0; i < numActiveOutputChans; ++i) { jassert (outBuffers[i] != nullptr); outputFormat[i].convertFromFloat (outBuffers[i], infos [numActiveInputChans + i].buffers[bi], samps); } } else { for (int i = 0; i < numActiveOutputChans; ++i) outputFormat[i].clear (infos[numActiveInputChans + i].buffers[bi], samps); } } if (postOutput) asioObject->outputReady(); } long asioMessagesCallback (long selector, long value) { switch (selector) { case kAsioSelectorSupported: if (value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest || value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor) return 1; break; case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); resetRequest(); return 1; case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); resetRequest(); return 1; case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); resetRequest(); return 1; case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1; case kAsioEngineVersion: return 2; case kAsioSupportsTimeInfo: case kAsioSupportsTimeCode: return 0; } return 0; } //============================================================================== template struct ASIOCallbackFunctions { static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback (ASIOTime*, long index, long) { if (currentASIODev[deviceIndex] != nullptr) currentASIODev[deviceIndex]->callback (index); return nullptr; } static void JUCE_ASIOCALLBACK bufferSwitchCallback (long index, long) { if (currentASIODev[deviceIndex] != nullptr) currentASIODev[deviceIndex]->callback (index); } static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*) { return currentASIODev[deviceIndex] != nullptr ? currentASIODev[deviceIndex]->asioMessagesCallback (selector, value) : 0; } static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate) { if (currentASIODev[deviceIndex] != nullptr) currentASIODev[deviceIndex]->resetRequest(); } static void setCallbacks (ASIOCallbacks& callbacks) noexcept { callbacks.bufferSwitch = &bufferSwitchCallback; callbacks.asioMessage = &asioMessagesCallback; callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; callbacks.sampleRateDidChange = &sampleRateChangedCallback; } static void setCallbacksForDevice (ASIOCallbacks& callbacks, ASIOAudioIODevice* device) noexcept { if (currentASIODev[deviceIndex] == device) setCallbacks (callbacks); else ASIOCallbackFunctions::setCallbacksForDevice (callbacks, device); } }; void setCallbackFunctions() noexcept { ASIOCallbackFunctions<0>::setCallbacksForDevice (callbacks, this); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) }; template <> struct ASIOAudioIODevice::ASIOCallbackFunctions { static void setCallbacksForDevice (ASIOCallbacks&, ASIOAudioIODevice*) noexcept {} }; //============================================================================== class ASIOAudioIODeviceType : public AudioIODeviceType { public: ASIOAudioIODeviceType() : AudioIODeviceType ("ASIO"), hasScanned (false) { } ~ASIOAudioIODeviceType() { masterReference.clear(); } //============================================================================== void scanForDevices() { hasScanned = true; deviceNames.clear(); classIds.clear(); HKEY hk = 0; int index = 0; if (RegOpenKey (HKEY_LOCAL_MACHINE, _T("software\\asio"), &hk) == ERROR_SUCCESS) { TCHAR name [256]; while (RegEnumKey (hk, index++, name, numElementsInArray (name)) == ERROR_SUCCESS) addDriverInfo (name, hk); RegCloseKey (hk); } } StringArray getDeviceNames (bool /*wantInputNames*/) const { jassert (hasScanned); // need to call scanForDevices() before doing this return deviceNames; } int getDefaultDeviceIndex (bool) const { jassert (hasScanned); // need to call scanForDevices() before doing this for (int i = deviceNames.size(); --i >= 0;) if (deviceNames[i].containsIgnoreCase ("asio4all")) return i; // asio4all is a safe choice for a default.. #if JUCE_DEBUG if (deviceNames.size() > 1 && deviceNames[0].containsIgnoreCase ("digidesign")) return 1; // (the digi m-box driver crashes the app when you run // it in the debugger, which can be a bit annoying) #endif return 0; } static int findFreeSlot() { for (int i = 0; i < numElementsInArray (currentASIODev); ++i) if (currentASIODev[i] == 0) return i; jassertfalse; // unfortunately you can only have a finite number // of ASIO devices open at the same time.. return -1; } int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { jassert (hasScanned); // need to call scanForDevices() before doing this return d == nullptr ? -1 : deviceNames.indexOf (d->getName()); } bool hasSeparateInputsAndOutputs() const { return false; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { // ASIO can't open two different devices for input and output - they must be the same one. jassert (inputDeviceName == outputDeviceName || outputDeviceName.isEmpty() || inputDeviceName.isEmpty()); jassert (hasScanned); // need to call scanForDevices() before doing this const String deviceName (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName); const int index = deviceNames.indexOf (deviceName); if (index >= 0) { const int freeSlot = findFreeSlot(); if (freeSlot >= 0) return new ASIOAudioIODevice (this, deviceName, classIds.getReference (index), freeSlot); } return nullptr; } void sendDeviceChangeToListeners() { callDeviceChangeListeners(); } WeakReference::Master masterReference; private: StringArray deviceNames; Array classIds; bool hasScanned; //============================================================================== static bool checkClassIsOk (const String& classId) { HKEY hk = 0; bool ok = false; if (RegOpenKey (HKEY_CLASSES_ROOT, _T("clsid"), &hk) == ERROR_SUCCESS) { int index = 0; TCHAR name [512]; while (RegEnumKey (hk, index++, name, numElementsInArray (name)) == ERROR_SUCCESS) { if (classId.equalsIgnoreCase (name)) { HKEY subKey, pathKey; if (RegOpenKeyEx (hk, name, 0, KEY_READ, &subKey) == ERROR_SUCCESS) { if (RegOpenKeyEx (subKey, _T("InprocServer32"), 0, KEY_READ, &pathKey) == ERROR_SUCCESS) { TCHAR pathName [1024] = { 0 }; DWORD dtype = REG_SZ; DWORD dsize = sizeof (pathName); if (RegQueryValueEx (pathKey, 0, 0, &dtype, (LPBYTE) pathName, &dsize) == ERROR_SUCCESS) // In older code, this used to check for the existance of the file, but there are situations // where our process doesn't have access to it, but where the driver still loads ok.. ok = (pathName[0] != 0); RegCloseKey (pathKey); } RegCloseKey (subKey); } break; } } RegCloseKey (hk); } return ok; } //============================================================================== void addDriverInfo (const String& keyName, HKEY hk) { HKEY subKey; if (RegOpenKeyEx (hk, keyName.toWideCharPointer(), 0, KEY_READ, &subKey) == ERROR_SUCCESS) { TCHAR buf [256] = { 0 }; DWORD dtype = REG_SZ; DWORD dsize = sizeof (buf); if (RegQueryValueEx (subKey, _T("clsid"), 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) { if (dsize > 0 && checkClassIsOk (buf)) { CLSID classId; if (CLSIDFromString ((LPOLESTR) buf, &classId) == S_OK) { dtype = REG_SZ; dsize = sizeof (buf); String deviceName; if (RegQueryValueEx (subKey, _T("description"), 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) deviceName = buf; else deviceName = keyName; JUCE_ASIO_LOG ("found " + deviceName); deviceNames.add (deviceName); classIds.add (classId); } } RegCloseKey (subKey); } } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODeviceType) }; void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType* type) { if (type != nullptr) type->sendDeviceChangeToListeners(); } AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); } juce_win32_AudioCDBurner.cpp000066400000000000000000000314301320201440200340070ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace CDBurnerHelpers { IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master) { CoInitialize (0); IDiscMaster* dm; IDiscRecorder* result = nullptr; if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IDiscMaster, (void**) &dm))) { if (SUCCEEDED (dm->Open())) { IEnumDiscRecorders* drEnum = nullptr; if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum))) { IDiscRecorder* dr = nullptr; DWORD dummy; int index = 0; while (drEnum->Next (1, &dr, &dummy) == S_OK) { if (indexToOpen == index) { result = dr; break; } else if (list != nullptr) { BSTR path; if (SUCCEEDED (dr->GetPath (&path))) list->add ((const WCHAR*) path); } ++index; dr->Release(); } drEnum->Release(); } if (master == 0) dm->Close(); } if (master != nullptr) *master = dm; else dm->Release(); } return result; } } //============================================================================== class AudioCDBurner::Pimpl : public ComBaseClassHelper , public Timer { public: Pimpl (AudioCDBurner& owner_, IDiscMaster* discMaster_, IDiscRecorder* discRecorder_) : owner (owner_), discMaster (discMaster_), discRecorder (discRecorder_), redbook (0), listener (0), progress (0), shouldCancel (false) { HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook); jassert (SUCCEEDED (hr)); hr = discMaster->SetActiveDiscRecorder (discRecorder); //jassert (SUCCEEDED (hr)); lastState = getDiskState(); startTimer (2000); } ~Pimpl() {} void releaseObjects() { discRecorder->Close(); if (redbook != nullptr) redbook->Release(); discRecorder->Release(); discMaster->Release(); Release(); } JUCE_COMRESULT QueryCancel (boolean* pbCancel) { if (listener != nullptr && ! shouldCancel) shouldCancel = listener->audioCDBurnProgress (progress); *pbCancel = shouldCancel; return S_OK; } JUCE_COMRESULT NotifyBlockProgress (long nCompleted, long nTotal) { progress = nCompleted / (float) nTotal; shouldCancel = listener != nullptr && listener->audioCDBurnProgress (progress); return E_NOTIMPL; } JUCE_COMRESULT NotifyPnPActivity (void) { return E_NOTIMPL; } JUCE_COMRESULT NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; } JUCE_COMRESULT NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; } JUCE_COMRESULT NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } JUCE_COMRESULT NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } JUCE_COMRESULT NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; } JUCE_COMRESULT NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; } class ScopedDiscOpener { public: ScopedDiscOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); } ~ScopedDiscOpener() { pimpl.discRecorder->Close(); } private: Pimpl& pimpl; JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener) }; DiskState getDiskState() { const ScopedDiscOpener opener (*this); long type, flags; HRESULT hr = discRecorder->QueryMediaType (&type, &flags); if (FAILED (hr)) return unknown; if (type != 0 && (flags & MEDIA_WRITABLE) != 0) return writableDiskPresent; if (type == 0) return noDisc; return readOnlyDiskPresent; } int getIntProperty (const LPOLESTR name, const int defaultReturn) const { ComSmartPtr prop; if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress()))) return defaultReturn; PROPSPEC iPropSpec; iPropSpec.ulKind = PRSPEC_LPWSTR; iPropSpec.lpwstr = name; PROPVARIANT iPropVariant; return FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant)) ? defaultReturn : (int) iPropVariant.lVal; } bool setIntProperty (const LPOLESTR name, const int value) const { ComSmartPtr prop; if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress()))) return false; PROPSPEC iPropSpec; iPropSpec.ulKind = PRSPEC_LPWSTR; iPropSpec.lpwstr = name; PROPVARIANT iPropVariant; if (FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant))) return false; iPropVariant.lVal = (long) value; return SUCCEEDED (prop->WriteMultiple (1, &iPropSpec, &iPropVariant, iPropVariant.vt)) && SUCCEEDED (discRecorder->SetRecorderProperties (prop)); } void timerCallback() override { const DiskState state = getDiskState(); if (state != lastState) { lastState = state; owner.sendChangeMessage(); } } AudioCDBurner& owner; DiskState lastState; IDiscMaster* discMaster; IDiscRecorder* discRecorder; IRedbookDiscMaster* redbook; AudioCDBurner::BurnProgressListener* listener; float progress; bool shouldCancel; }; //============================================================================== AudioCDBurner::AudioCDBurner (const int deviceIndex) { IDiscMaster* discMaster = nullptr; IDiscRecorder* discRecorder = CDBurnerHelpers::enumCDBurners (0, deviceIndex, &discMaster); if (discRecorder != nullptr) pimpl = new Pimpl (*this, discMaster, discRecorder); } AudioCDBurner::~AudioCDBurner() { if (pimpl != nullptr) pimpl.release()->releaseObjects(); } StringArray AudioCDBurner::findAvailableDevices() { StringArray devs; CDBurnerHelpers::enumCDBurners (&devs, -1, 0); return devs; } AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) { ScopedPointer b (new AudioCDBurner (deviceIndex)); if (b->pimpl == 0) b = nullptr; return b.release(); } AudioCDBurner::DiskState AudioCDBurner::getDiskState() const { return pimpl->getDiskState(); } bool AudioCDBurner::isDiskPresent() const { return getDiskState() == writableDiskPresent; } bool AudioCDBurner::openTray() { const Pimpl::ScopedDiscOpener opener (*pimpl); return SUCCEEDED (pimpl->discRecorder->Eject()); } AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds) { const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds; DiskState oldState = getDiskState(); DiskState newState = oldState; while (newState == oldState && Time::currentTimeMillis() < timeout) { newState = getDiskState(); Thread::sleep (jmin (250, (int) (timeout - Time::currentTimeMillis()))); } return newState; } Array AudioCDBurner::getAvailableWriteSpeeds() const { Array results; const int maxSpeed = pimpl->getIntProperty (L"MaxWriteSpeed", 1); const int speeds[] = { 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 64, 80 }; for (int i = 0; i < numElementsInArray (speeds); ++i) if (speeds[i] <= maxSpeed) results.add (speeds[i]); results.addIfNotAlreadyThere (maxSpeed); return results; } bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled) { if (pimpl->getIntProperty (L"BufferUnderrunFreeCapable", 0) == 0) return false; pimpl->setIntProperty (L"EnableBufferUnderrunFree", shouldBeEnabled ? -1 : 0); return pimpl->getIntProperty (L"EnableBufferUnderrunFree", 0) != 0; } int AudioCDBurner::getNumAvailableAudioBlocks() const { long blocksFree = 0; pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree); return blocksFree; } String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards, bool performFakeBurnForTesting, int writeSpeed) { pimpl->setIntProperty (L"WriteSpeed", writeSpeed > 0 ? writeSpeed : -1); pimpl->listener = listener; pimpl->progress = 0; pimpl->shouldCancel = false; UINT_PTR cookie; HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie); hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting, ejectDiscAfterwards); String error; if (hr != S_OK) { const char* e = "Couldn't open or write to the CD device"; if (hr == IMAPI_E_USERABORT) e = "User cancelled the write operation"; else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN) e = "No Disk present"; error = e; } pimpl->discMaster->ProgressUnadvise (cookie); pimpl->listener = 0; return error; } bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) { if (audioSource == 0) return false; ScopedPointer source (audioSource); long bytesPerBlock; HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock); const int samplesPerBlock = bytesPerBlock / 4; bool ok = true; hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); HeapBlock buffer (bytesPerBlock); AudioSampleBuffer sourceBuffer (2, samplesPerBlock); int samplesDone = 0; source->prepareToPlay (samplesPerBlock, 44100.0); while (ok) { { AudioSourceChannelInfo info (&sourceBuffer, 0, samplesPerBlock); sourceBuffer.clear(); source->getNextAudioBlock (info); } buffer.clear (bytesPerBlock); typedef AudioData::Pointer CDSampleFormat; typedef AudioData::Pointer SourceSampleFormat; CDSampleFormat left (buffer, 2); left.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (0)), samplesPerBlock); CDSampleFormat right (buffer + 2, 2); right.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (1)), samplesPerBlock); hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); if (FAILED (hr)) ok = false; samplesDone += samplesPerBlock; if (samplesDone >= numSamples) break; } hr = pimpl->redbook->CloseAudioTrack(); return ok && hr == S_OK; } juce_win32_AudioCDReader.cpp000066400000000000000000001111221320201440200337510ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace CDReaderHelpers { #define FILE_ANY_ACCESS 0 #ifndef FILE_READ_ACCESS #define FILE_READ_ACCESS 1 #endif #ifndef FILE_WRITE_ACCESS #define FILE_WRITE_ACCESS 2 #endif #define METHOD_BUFFERED 0 #define IOCTL_SCSI_BASE 4 #define SCSI_IOCTL_DATA_OUT 0 #define SCSI_IOCTL_DATA_IN 1 #define SCSI_IOCTL_DATA_UNSPECIFIED 2 #define CTL_CODE2(DevType, Function, Method, Access) (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) #define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE2( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) #define IOCTL_SCSI_GET_ADDRESS CTL_CODE2( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS ) #define SENSE_LEN 14 #define SRB_ENABLE_RESIDUAL_COUNT 0x04 #define SRB_DIR_IN 0x08 #define SRB_DIR_OUT 0x10 #define SRB_EVENT_NOTIFY 0x40 #define SC_HA_INQUIRY 0x00 #define SC_GET_DEV_TYPE 0x01 #define SC_EXEC_SCSI_CMD 0x02 #define SS_PENDING 0x00 #define SS_COMP 0x01 #define SS_ERR 0x04 enum { READTYPE_ANY = 0, READTYPE_ATAPI1 = 1, READTYPE_ATAPI2 = 2, READTYPE_READ6 = 3, READTYPE_READ10 = 4, READTYPE_READ_D8 = 5, READTYPE_READ_D4 = 6, READTYPE_READ_D4_1 = 7, READTYPE_READ10_2 = 8 }; struct SCSI_PASS_THROUGH { USHORT Length; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR CdbLength; UCHAR SenseInfoLength; UCHAR DataIn; ULONG DataTransferLength; ULONG TimeOutValue; ULONG DataBufferOffset; ULONG SenseInfoOffset; UCHAR Cdb[16]; }; struct SCSI_PASS_THROUGH_DIRECT { USHORT Length; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR CdbLength; UCHAR SenseInfoLength; UCHAR DataIn; ULONG DataTransferLength; ULONG TimeOutValue; PVOID DataBuffer; ULONG SenseInfoOffset; UCHAR Cdb[16]; }; struct SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER { SCSI_PASS_THROUGH_DIRECT spt; ULONG Filler; UCHAR ucSenseBuf[32]; }; struct SCSI_ADDRESS { ULONG Length; UCHAR PortNumber; UCHAR PathId; UCHAR TargetId; UCHAR Lun; }; #pragma pack(1) struct SRB_GDEVBlock { BYTE SRB_Cmd; BYTE SRB_Status; BYTE SRB_HaID; BYTE SRB_Flags; DWORD SRB_Hdr_Rsvd; BYTE SRB_Target; BYTE SRB_Lun; BYTE SRB_DeviceType; BYTE SRB_Rsvd1; BYTE pad[68]; }; struct SRB_ExecSCSICmd { BYTE SRB_Cmd; BYTE SRB_Status; BYTE SRB_HaID; BYTE SRB_Flags; DWORD SRB_Hdr_Rsvd; BYTE SRB_Target; BYTE SRB_Lun; WORD SRB_Rsvd1; DWORD SRB_BufLen; BYTE *SRB_BufPointer; BYTE SRB_SenseLen; BYTE SRB_CDBLen; BYTE SRB_HaStat; BYTE SRB_TargStat; VOID *SRB_PostProc; BYTE SRB_Rsvd2[20]; BYTE CDBByte[16]; BYTE SenseArea[SENSE_LEN + 2]; }; struct SRB { BYTE SRB_Cmd; BYTE SRB_Status; BYTE SRB_HaId; BYTE SRB_Flags; DWORD SRB_Hdr_Rsvd; }; struct TOCTRACK { BYTE rsvd; BYTE ADR; BYTE trackNumber; BYTE rsvd2; BYTE addr[4]; }; struct TOC { WORD tocLen; BYTE firstTrack; BYTE lastTrack; TOCTRACK tracks[100]; }; #pragma pack() //============================================================================== struct CDDeviceDescription { CDDeviceDescription() : ha (0), tgt (0), lun (0), scsiDriveLetter (0) { } void createDescription (const char* data) { description << String (data + 8, 8).trim() // vendor << ' ' << String (data + 16, 16).trim() // product id << ' ' << String (data + 32, 4).trim(); // rev } String description; BYTE ha, tgt, lun; char scsiDriveLetter; // will be 0 if not using scsi }; //============================================================================== class CDReadBuffer { public: CDReadBuffer (const int numberOfFrames) : startFrame (0), numFrames (0), dataStartOffset (0), dataLength (0), bufferSize (2352 * numberOfFrames), index (0), buffer (bufferSize), wantsIndex (false) { } bool isZero() const noexcept { for (int i = 0; i < dataLength; ++i) if (buffer [dataStartOffset + i] != 0) return false; return true; } int startFrame, numFrames, dataStartOffset; int dataLength, bufferSize, index; HeapBlock buffer; bool wantsIndex; }; class CDDeviceHandle; //============================================================================== class CDController { public: CDController() : initialised (false) {} virtual ~CDController() {} virtual bool read (CDReadBuffer&) = 0; virtual void shutDown() {} bool readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer = 0); int getLastIndex(); public: CDDeviceHandle* deviceInfo; int framesToCheck, framesOverlap; bool initialised; void prepare (SRB_ExecSCSICmd& s); void perform (SRB_ExecSCSICmd& s); void setPaused (bool paused); }; //============================================================================== class CDDeviceHandle { public: CDDeviceHandle (const CDDeviceDescription& device, HANDLE scsiHandle_) : info (device), scsiHandle (scsiHandle_), readType (READTYPE_ANY) { } ~CDDeviceHandle() { if (controller != nullptr) { controller->shutDown(); controller = 0; } if (scsiHandle != 0) CloseHandle (scsiHandle); } bool readTOC (TOC* lpToc); bool readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer = 0); void openDrawer (bool shouldBeOpen); void performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s); CDDeviceDescription info; HANDLE scsiHandle; BYTE readType; private: ScopedPointer controller; bool testController (int readType, CDController* newController, CDReadBuffer& bufferToUse); }; //============================================================================== HANDLE createSCSIDeviceHandle (const char driveLetter) { TCHAR devicePath[] = { '\\', '\\', '.', '\\', driveLetter, ':', 0, 0 }; DWORD flags = GENERIC_READ | GENERIC_WRITE; HANDLE h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (h == INVALID_HANDLE_VALUE) { flags ^= GENERIC_WRITE; h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } return h; } void findCDDevices (Array& list) { for (char driveLetter = 'b'; driveLetter <= 'z'; ++driveLetter) { TCHAR drivePath[] = { driveLetter, ':', '\\', 0, 0 }; if (GetDriveType (drivePath) == DRIVE_CDROM) { HANDLE h = createSCSIDeviceHandle (driveLetter); if (h != INVALID_HANDLE_VALUE) { char buffer[100] = { 0 }; SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER p = { 0 }; p.spt.Length = sizeof (SCSI_PASS_THROUGH); p.spt.CdbLength = 6; p.spt.SenseInfoLength = 24; p.spt.DataIn = SCSI_IOCTL_DATA_IN; p.spt.DataTransferLength = sizeof (buffer); p.spt.TimeOutValue = 2; p.spt.DataBuffer = buffer; p.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); p.spt.Cdb[0] = 0x12; p.spt.Cdb[4] = 100; DWORD bytesReturned = 0; if (DeviceIoControl (h, IOCTL_SCSI_PASS_THROUGH_DIRECT, &p, sizeof (p), &p, sizeof (p), &bytesReturned, 0) != 0) { CDDeviceDescription dev; dev.scsiDriveLetter = driveLetter; dev.createDescription (buffer); SCSI_ADDRESS scsiAddr = { 0 }; scsiAddr.Length = sizeof (scsiAddr); if (DeviceIoControl (h, IOCTL_SCSI_GET_ADDRESS, 0, 0, &scsiAddr, sizeof (scsiAddr), &bytesReturned, 0) != 0) { dev.ha = scsiAddr.PortNumber; dev.tgt = scsiAddr.TargetId; dev.lun = scsiAddr.Lun; list.add (dev); } } CloseHandle (h); } } } } DWORD performScsiPassThroughCommand (SRB_ExecSCSICmd* const srb, const char driveLetter, HANDLE& deviceHandle, const bool retryOnFailure) { SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER s = { 0 }; s.spt.Length = sizeof (SCSI_PASS_THROUGH); s.spt.CdbLength = srb->SRB_CDBLen; s.spt.DataIn = (BYTE) ((srb->SRB_Flags & SRB_DIR_IN) ? SCSI_IOCTL_DATA_IN : ((srb->SRB_Flags & SRB_DIR_OUT) ? SCSI_IOCTL_DATA_OUT : SCSI_IOCTL_DATA_UNSPECIFIED)); s.spt.DataTransferLength = srb->SRB_BufLen; s.spt.TimeOutValue = 5; s.spt.DataBuffer = srb->SRB_BufPointer; s.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); memcpy (s.spt.Cdb, srb->CDBByte, srb->SRB_CDBLen); srb->SRB_Status = SS_ERR; srb->SRB_TargStat = 0x0004; DWORD bytesReturned = 0; if (DeviceIoControl (deviceHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT, &s, sizeof (s), &s, sizeof (s), &bytesReturned, 0) != 0) { srb->SRB_Status = SS_COMP; } else if (retryOnFailure) { const DWORD error = GetLastError(); if ((error == ERROR_MEDIA_CHANGED) || (error == ERROR_INVALID_HANDLE)) { if (error != ERROR_INVALID_HANDLE) CloseHandle (deviceHandle); deviceHandle = createSCSIDeviceHandle (driveLetter); return performScsiPassThroughCommand (srb, driveLetter, deviceHandle, false); } } return srb->SRB_Status; } //============================================================================== // Controller types.. class ControllerType1 : public CDController { public: ControllerType1() {} bool read (CDReadBuffer& rb) { if (rb.numFrames * 2352 > rb.bufferSize) return false; SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; s.SRB_BufLen = rb.bufferSize; s.SRB_BufPointer = rb.buffer; s.SRB_CDBLen = 12; s.CDBByte[0] = 0xBE; s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF); s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF); s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF); s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF); s.CDBByte[9] = (BYTE) (deviceInfo->readType == READTYPE_ATAPI1 ? 0x10 : 0xF0); perform (s); if (s.SRB_Status != SS_COMP) return false; rb.dataLength = rb.numFrames * 2352; rb.dataStartOffset = 0; return true; } }; //============================================================================== class ControllerType2 : public CDController { public: ControllerType2() {} void shutDown() { if (initialised) { BYTE bufPointer[] = { 0, 0, 0, 8, 83, 0, 0, 0, 0, 0, 8, 0 }; SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_EVENT_NOTIFY | SRB_ENABLE_RESIDUAL_COUNT; s.SRB_BufLen = 0x0C; s.SRB_BufPointer = bufPointer; s.SRB_CDBLen = 6; s.CDBByte[0] = 0x15; s.CDBByte[4] = 0x0C; perform (s); } } bool init() { SRB_ExecSCSICmd s; s.SRB_Status = SS_ERR; if (deviceInfo->readType == READTYPE_READ10_2) { BYTE bufPointer1[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 35, 6, 0, 0, 0, 0, 0, 128 }; BYTE bufPointer2[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 1, 6, 32, 7, 0, 0, 0, 0 }; for (int i = 0; i < 2; ++i) { prepare (s); s.SRB_Flags = SRB_EVENT_NOTIFY; s.SRB_BufLen = 0x14; s.SRB_BufPointer = (i == 0) ? bufPointer1 : bufPointer2; s.SRB_CDBLen = 6; s.CDBByte[0] = 0x15; s.CDBByte[1] = 0x10; s.CDBByte[4] = 0x14; perform (s); if (s.SRB_Status != SS_COMP) return false; } } else { BYTE bufPointer[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48 }; prepare (s); s.SRB_Flags = SRB_EVENT_NOTIFY; s.SRB_BufLen = 0x0C; s.SRB_BufPointer = bufPointer; s.SRB_CDBLen = 6; s.CDBByte[0] = 0x15; s.CDBByte[4] = 0x0C; perform (s); } return s.SRB_Status == SS_COMP; } bool read (CDReadBuffer& rb) { if (rb.numFrames * 2352 > rb.bufferSize) return false; if (! initialised) { initialised = init(); if (! initialised) return false; } SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; s.SRB_BufLen = rb.bufferSize; s.SRB_BufPointer = rb.buffer; s.SRB_CDBLen = 10; s.CDBByte[0] = 0x28; s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5); s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF); s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF); s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF); s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF); perform (s); if (s.SRB_Status != SS_COMP) return false; rb.dataLength = rb.numFrames * 2352; rb.dataStartOffset = 0; return true; } }; //============================================================================== class ControllerType3 : public CDController { public: ControllerType3() {} bool read (CDReadBuffer& rb) { if (rb.numFrames * 2352 > rb.bufferSize) return false; if (! initialised) { setPaused (false); initialised = true; } SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; s.SRB_BufLen = rb.numFrames * 2352; s.SRB_BufPointer = rb.buffer; s.SRB_CDBLen = 12; s.CDBByte[0] = 0xD8; s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF); s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF); s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF); s.CDBByte[9] = (BYTE) (rb.numFrames & 0xFF); perform (s); if (s.SRB_Status != SS_COMP) return false; rb.dataLength = rb.numFrames * 2352; rb.dataStartOffset = 0; return true; } }; //============================================================================== class ControllerType4 : public CDController { public: ControllerType4() {} bool selectD4Mode() { BYTE bufPointer[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 48 }; SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_EVENT_NOTIFY; s.SRB_CDBLen = 6; s.SRB_BufLen = 12; s.SRB_BufPointer = bufPointer; s.CDBByte[0] = 0x15; s.CDBByte[1] = 0x10; s.CDBByte[4] = 0x08; perform (s); return s.SRB_Status == SS_COMP; } bool read (CDReadBuffer& rb) { if (rb.numFrames * 2352 > rb.bufferSize) return false; if (! initialised) { setPaused (true); if (deviceInfo->readType == READTYPE_READ_D4_1) selectD4Mode(); initialised = true; } SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; s.SRB_BufLen = rb.bufferSize; s.SRB_BufPointer = rb.buffer; s.SRB_CDBLen = 10; s.CDBByte[0] = 0xD4; s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF); s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF); s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF); s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF); perform (s); if (s.SRB_Status != SS_COMP) return false; rb.dataLength = rb.numFrames * 2352; rb.dataStartOffset = 0; return true; } }; //============================================================================== void CDController::prepare (SRB_ExecSCSICmd& s) { zerostruct (s); s.SRB_Cmd = SC_EXEC_SCSI_CMD; s.SRB_HaID = deviceInfo->info.ha; s.SRB_Target = deviceInfo->info.tgt; s.SRB_Lun = deviceInfo->info.lun; s.SRB_SenseLen = SENSE_LEN; } void CDController::perform (SRB_ExecSCSICmd& s) { s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0); deviceInfo->performScsiCommand (s.SRB_PostProc, s); } void CDController::setPaused (bool paused) { SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_EVENT_NOTIFY; s.SRB_CDBLen = 10; s.CDBByte[0] = 0x4B; s.CDBByte[8] = (BYTE) (paused ? 0 : 1); perform (s); } bool CDController::readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer) { if (overlapBuffer != nullptr) { const bool canDoJitter = (overlapBuffer->bufferSize >= 2352 * framesToCheck); const bool doJitter = canDoJitter && ! overlapBuffer->isZero(); if (doJitter && overlapBuffer->startFrame > 0 && overlapBuffer->numFrames > 0 && overlapBuffer->dataLength > 0) { const int numFrames = rb.numFrames; if (overlapBuffer->startFrame == (rb.startFrame - framesToCheck)) { rb.startFrame -= framesOverlap; if (framesToCheck < framesOverlap && numFrames + framesOverlap <= rb.bufferSize / 2352) rb.numFrames += framesOverlap; } else { overlapBuffer->dataLength = 0; overlapBuffer->startFrame = 0; overlapBuffer->numFrames = 0; } } if (! read (rb)) return false; if (doJitter) { const int checkLen = framesToCheck * 2352; const int maxToCheck = rb.dataLength - checkLen; if (overlapBuffer->dataLength == 0 || overlapBuffer->isZero()) return true; BYTE* const p = overlapBuffer->buffer + overlapBuffer->dataStartOffset; bool found = false; for (int i = 0; i < maxToCheck; ++i) { if (memcmp (p, rb.buffer + i, checkLen) == 0) { i += checkLen; rb.dataStartOffset = i; rb.dataLength -= i; rb.startFrame = overlapBuffer->startFrame + framesToCheck; found = true; break; } } rb.numFrames = rb.dataLength / 2352; rb.dataLength = 2352 * rb.numFrames; if (! found) return false; } if (canDoJitter) { memcpy (overlapBuffer->buffer, rb.buffer + rb.dataStartOffset + 2352 * (rb.numFrames - framesToCheck), 2352 * framesToCheck); overlapBuffer->startFrame = rb.startFrame + rb.numFrames - framesToCheck; overlapBuffer->numFrames = framesToCheck; overlapBuffer->dataLength = 2352 * framesToCheck; overlapBuffer->dataStartOffset = 0; } else { overlapBuffer->startFrame = 0; overlapBuffer->numFrames = 0; overlapBuffer->dataLength = 0; } return true; } return read (rb); } int CDController::getLastIndex() { char qdata[100]; SRB_ExecSCSICmd s; prepare (s); s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; s.SRB_BufLen = sizeof (qdata); s.SRB_BufPointer = (BYTE*) qdata; s.SRB_CDBLen = 12; s.CDBByte[0] = 0x42; s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5); s.CDBByte[2] = 64; s.CDBByte[3] = 1; // get current position s.CDBByte[7] = 0; s.CDBByte[8] = (BYTE) sizeof (qdata); perform (s); return s.SRB_Status == SS_COMP ? qdata[7] : 0; } //============================================================================== bool CDDeviceHandle::readTOC (TOC* lpToc) { SRB_ExecSCSICmd s = { 0 }; s.SRB_Cmd = SC_EXEC_SCSI_CMD; s.SRB_HaID = info.ha; s.SRB_Target = info.tgt; s.SRB_Lun = info.lun; s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; s.SRB_BufLen = 0x324; s.SRB_BufPointer = (BYTE*) lpToc; s.SRB_SenseLen = 0x0E; s.SRB_CDBLen = 0x0A; s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0); s.CDBByte[0] = 0x43; s.CDBByte[1] = 0x00; s.CDBByte[7] = 0x03; s.CDBByte[8] = 0x24; performScsiCommand (s.SRB_PostProc, s); return (s.SRB_Status == SS_COMP); } void CDDeviceHandle::performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s) { ResetEvent (event); DWORD status = performScsiPassThroughCommand ((SRB_ExecSCSICmd*) &s, info.scsiDriveLetter, scsiHandle, true); if (status == SS_PENDING) WaitForSingleObject (event, 4000); CloseHandle (event); } bool CDDeviceHandle::readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer) { if (controller == 0) { testController (READTYPE_ATAPI2, new ControllerType1(), buffer) || testController (READTYPE_ATAPI1, new ControllerType1(), buffer) || testController (READTYPE_READ10_2, new ControllerType2(), buffer) || testController (READTYPE_READ10, new ControllerType2(), buffer) || testController (READTYPE_READ_D8, new ControllerType3(), buffer) || testController (READTYPE_READ_D4, new ControllerType4(), buffer) || testController (READTYPE_READ_D4_1, new ControllerType4(), buffer); } buffer.index = 0; if (controller != nullptr && controller->readAudio (buffer, overlapBuffer)) { if (buffer.wantsIndex) buffer.index = controller->getLastIndex(); return true; } return false; } void CDDeviceHandle::openDrawer (bool shouldBeOpen) { if (shouldBeOpen) { if (controller != nullptr) { controller->shutDown(); controller = nullptr; } if (scsiHandle != 0) { CloseHandle (scsiHandle); scsiHandle = 0; } } SRB_ExecSCSICmd s = { 0 }; s.SRB_Cmd = SC_EXEC_SCSI_CMD; s.SRB_HaID = info.ha; s.SRB_Target = info.tgt; s.SRB_Lun = info.lun; s.SRB_SenseLen = SENSE_LEN; s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; s.SRB_BufLen = 0; s.SRB_BufPointer = 0; s.SRB_CDBLen = 12; s.CDBByte[0] = 0x1b; s.CDBByte[1] = (BYTE) (info.lun << 5); s.CDBByte[4] = (BYTE) (shouldBeOpen ? 2 : 3); s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0); performScsiCommand (s.SRB_PostProc, s); } bool CDDeviceHandle::testController (const int type, CDController* const newController, CDReadBuffer& rb) { controller = newController; readType = (BYTE) type; controller->deviceInfo = this; controller->framesToCheck = 1; controller->framesOverlap = 3; bool passed = false; memset (rb.buffer, 0xcd, rb.bufferSize); if (controller->read (rb)) { passed = true; int* p = (int*) (rb.buffer + rb.dataStartOffset); int wrong = 0; for (int i = rb.dataLength / 4; --i >= 0;) { if (*p++ == (int) 0xcdcdcdcd) { if (++wrong == 4) { passed = false; break; } } else { wrong = 0; } } } if (! passed) { controller->shutDown(); controller = nullptr; } return passed; } //============================================================================== struct CDDeviceWrapper { CDDeviceWrapper (const CDDeviceDescription& device, HANDLE scsiHandle) : deviceHandle (device, scsiHandle), overlapBuffer (3), jitter (false) { // xxx jitter never seemed to actually be enabled (??) } CDDeviceHandle deviceHandle; CDReadBuffer overlapBuffer; bool jitter; }; //============================================================================== int getAddressOfTrack (const TOCTRACK& t) noexcept { return (((DWORD) t.addr[0]) << 24) + (((DWORD) t.addr[1]) << 16) + (((DWORD) t.addr[2]) << 8) + ((DWORD) t.addr[3]); } const int samplesPerFrame = 44100 / 75; const int bytesPerFrame = samplesPerFrame * 4; const int framesPerIndexRead = 4; } //============================================================================== StringArray AudioCDReader::getAvailableCDNames() { using namespace CDReaderHelpers; StringArray results; Array list; findCDDevices (list); for (int i = 0; i < list.size(); ++i) { String s; if (list[i].scsiDriveLetter > 0) s << String::charToString (list[i].scsiDriveLetter).toUpperCase() << ": "; s << list[i].description; results.add (s); } return results; } AudioCDReader* AudioCDReader::createReaderForCD (const int deviceIndex) { using namespace CDReaderHelpers; Array list; findCDDevices (list); if (isPositiveAndBelow (deviceIndex, list.size())) { HANDLE h = createSCSIDeviceHandle (list [deviceIndex].scsiDriveLetter); if (h != INVALID_HANDLE_VALUE) { ScopedPointer cd (new AudioCDReader (new CDDeviceWrapper (list [deviceIndex], h))); if (cd->lengthInSamples > 0) return cd.release(); } } return nullptr; } AudioCDReader::AudioCDReader (void* handle_) : AudioFormatReader (0, "CD Audio"), handle (handle_), indexingEnabled (false), lastIndex (0), firstFrameInBuffer (0), samplesInBuffer (0) { using namespace CDReaderHelpers; jassert (handle_ != nullptr); refreshTrackLengths(); sampleRate = 44100.0; bitsPerSample = 16; numChannels = 2; usesFloatingPointData = false; buffer.setSize (4 * bytesPerFrame, true); } AudioCDReader::~AudioCDReader() { using namespace CDReaderHelpers; CDDeviceWrapper* const device = static_cast (handle); delete device; } bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { using namespace CDReaderHelpers; CDDeviceWrapper* const device = static_cast (handle); bool ok = true; while (numSamples > 0) { const int bufferStartSample = firstFrameInBuffer * samplesPerFrame; const int bufferEndSample = bufferStartSample + samplesInBuffer; if (startSampleInFile >= bufferStartSample && startSampleInFile < bufferEndSample) { const int toDo = (int) jmin ((int64) numSamples, bufferEndSample - startSampleInFile); int* const l = destSamples[0] + startOffsetInDestBuffer; int* const r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr; const short* src = (const short*) buffer.getData(); src += 2 * (startSampleInFile - bufferStartSample); for (int i = 0; i < toDo; ++i) { l[i] = src [i << 1] << 16; if (r != nullptr) r[i] = src [(i << 1) + 1] << 16; } startOffsetInDestBuffer += toDo; startSampleInFile += toDo; numSamples -= toDo; } else { const int framesInBuffer = (int) (buffer.getSize() / bytesPerFrame); const int frameNeeded = (int) (startSampleInFile / samplesPerFrame); if (firstFrameInBuffer + framesInBuffer != frameNeeded) { device->overlapBuffer.dataLength = 0; device->overlapBuffer.startFrame = 0; device->overlapBuffer.numFrames = 0; device->jitter = false; } firstFrameInBuffer = frameNeeded; lastIndex = 0; CDReadBuffer readBuffer (framesInBuffer + 4); readBuffer.wantsIndex = indexingEnabled; int i; for (i = 5; --i >= 0;) { readBuffer.startFrame = frameNeeded; readBuffer.numFrames = framesInBuffer; if (device->deviceHandle.readAudio (readBuffer, device->jitter ? &device->overlapBuffer : 0)) break; else device->overlapBuffer.dataLength = 0; } if (i >= 0) { buffer.copyFrom (readBuffer.buffer + readBuffer.dataStartOffset, 0, readBuffer.dataLength); samplesInBuffer = readBuffer.dataLength >> 2; lastIndex = readBuffer.index; } else { int* l = destSamples[0] + startOffsetInDestBuffer; int* r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr; while (--numSamples >= 0) { *l++ = 0; if (r != nullptr) *r++ = 0; } // sometimes the read fails for just the very last couple of blocks, so // we'll ignore and errors in the last half-second of the disk.. ok = startSampleInFile > (trackStartSamples [getNumTracks()] - 20000); break; } } } return ok; } bool AudioCDReader::isCDStillPresent() const { using namespace CDReaderHelpers; TOC toc = { 0 }; return static_cast (handle)->deviceHandle.readTOC (&toc); } void AudioCDReader::refreshTrackLengths() { using namespace CDReaderHelpers; trackStartSamples.clear(); zeromem (audioTracks, sizeof (audioTracks)); TOC toc = { 0 }; if (static_cast (handle)->deviceHandle.readTOC (&toc)) { int numTracks = 1 + toc.lastTrack - toc.firstTrack; for (int i = 0; i <= numTracks; ++i) { trackStartSamples.add (samplesPerFrame * getAddressOfTrack (toc.tracks [i])); audioTracks [i] = ((toc.tracks[i].ADR & 4) == 0); } } lengthInSamples = getPositionOfTrackStart (getNumTracks()); } bool AudioCDReader::isTrackAudio (int trackNum) const { return trackNum >= 0 && trackNum < getNumTracks() && audioTracks [trackNum]; } void AudioCDReader::enableIndexScanning (bool b) { indexingEnabled = b; } int AudioCDReader::getLastIndex() const { return lastIndex; } int AudioCDReader::getIndexAt (int samplePos) { using namespace CDReaderHelpers; CDDeviceWrapper* const device = static_cast (handle); const int frameNeeded = samplePos / samplesPerFrame; device->overlapBuffer.dataLength = 0; device->overlapBuffer.startFrame = 0; device->overlapBuffer.numFrames = 0; device->jitter = false; firstFrameInBuffer = 0; lastIndex = 0; CDReadBuffer readBuffer (4 + framesPerIndexRead); readBuffer.wantsIndex = true; int i; for (i = 5; --i >= 0;) { readBuffer.startFrame = frameNeeded; readBuffer.numFrames = framesPerIndexRead; if (device->deviceHandle.readAudio (readBuffer)) break; } if (i >= 0) return readBuffer.index; return -1; } Array AudioCDReader::findIndexesInTrack (const int trackNumber) { using namespace CDReaderHelpers; Array indexes; const int trackStart = getPositionOfTrackStart (trackNumber); const int trackEnd = getPositionOfTrackStart (trackNumber + 1); bool needToScan = true; if (trackEnd - trackStart > 20 * 44100) { // check the end of the track for indexes before scanning the whole thing needToScan = false; int pos = jmax (trackStart, trackEnd - 44100 * 5); bool seenAnIndex = false; while (pos <= trackEnd - samplesPerFrame) { const int index = getIndexAt (pos); if (index == 0) { // lead-out, so skip back a bit if we've not found any indexes yet.. if (seenAnIndex) break; pos -= 44100 * 5; if (pos < trackStart) break; } else { if (index > 0) seenAnIndex = true; if (index > 1) { needToScan = true; break; } pos += samplesPerFrame * framesPerIndexRead; } } } if (needToScan) { CDDeviceWrapper* const device = static_cast (handle); int pos = trackStart; int last = -1; while (pos < trackEnd - samplesPerFrame * 10) { const int frameNeeded = pos / samplesPerFrame; device->overlapBuffer.dataLength = 0; device->overlapBuffer.startFrame = 0; device->overlapBuffer.numFrames = 0; device->jitter = false; firstFrameInBuffer = 0; CDReadBuffer readBuffer (4); readBuffer.wantsIndex = true; int i; for (i = 5; --i >= 0;) { readBuffer.startFrame = frameNeeded; readBuffer.numFrames = framesPerIndexRead; if (device->deviceHandle.readAudio (readBuffer)) break; } if (i < 0) break; if (readBuffer.index > last && readBuffer.index > 1) { last = readBuffer.index; indexes.add (pos); } pos += samplesPerFrame * framesPerIndexRead; } indexes.removeFirstMatchingValue (trackStart); } return indexes; } void AudioCDReader::ejectDisk() { using namespace CDReaderHelpers; static_cast (handle)->deviceHandle.openDrawer (true); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp000066400000000000000000001270541320201440200336730ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ } // (juce namespace) extern "C" { // Declare just the minimum number of interfaces for the DSound objects that we need.. typedef struct typeDSBUFFERDESC { DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; LPWAVEFORMATEX lpwfxFormat; GUID guid3DAlgorithm; } DSBUFFERDESC; struct IDirectSoundBuffer; #undef INTERFACE #define INTERFACE IDirectSound DECLARE_INTERFACE_(IDirectSound, IUnknown) { STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; STDMETHOD_(ULONG,AddRef) (THIS) PURE; STDMETHOD_(ULONG,Release) (THIS) PURE; STDMETHOD(CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE; STDMETHOD(GetCaps) (THIS_ void*) PURE; STDMETHOD(DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE; STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; STDMETHOD(Compact) (THIS) PURE; STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE; STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE; STDMETHOD(Initialize) (THIS_ const GUID*) PURE; }; #undef INTERFACE #define INTERFACE IDirectSoundBuffer DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown) { STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; STDMETHOD_(ULONG,AddRef) (THIS) PURE; STDMETHOD_(ULONG,Release) (THIS) PURE; STDMETHOD(GetCaps) (THIS_ void*) PURE; STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; STDMETHOD(GetVolume) (THIS_ LPLONG) PURE; STDMETHOD(GetPan) (THIS_ LPLONG) PURE; STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE; STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; STDMETHOD(Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE; STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE; STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE; STDMETHOD(SetFormat) (THIS_ const WAVEFORMATEX*) PURE; STDMETHOD(SetVolume) (THIS_ LONG) PURE; STDMETHOD(SetPan) (THIS_ LONG) PURE; STDMETHOD(SetFrequency) (THIS_ DWORD) PURE; STDMETHOD(Stop) (THIS) PURE; STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; STDMETHOD(Restore) (THIS) PURE; }; //============================================================================== typedef struct typeDSCBUFFERDESC { DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; LPWAVEFORMATEX lpwfxFormat; } DSCBUFFERDESC; struct IDirectSoundCaptureBuffer; #undef INTERFACE #define INTERFACE IDirectSoundCapture DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown) { STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; STDMETHOD_(ULONG,AddRef) (THIS) PURE; STDMETHOD_(ULONG,Release) (THIS) PURE; STDMETHOD(CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE; STDMETHOD(GetCaps) (THIS_ void*) PURE; STDMETHOD(Initialize) (THIS_ const GUID*) PURE; }; #undef INTERFACE #define INTERFACE IDirectSoundCaptureBuffer DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown) { STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; STDMETHOD_(ULONG,AddRef) (THIS) PURE; STDMETHOD_(ULONG,Release) (THIS) PURE; STDMETHOD(GetCaps) (THIS_ void*) PURE; STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; STDMETHOD(Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE; STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; STDMETHOD(Start) (THIS_ DWORD) PURE; STDMETHOD(Stop) (THIS) PURE; STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; }; #undef INTERFACE } namespace juce { //============================================================================== namespace DSoundLogging { String getErrorMessage (HRESULT hr) { const char* result = nullptr; switch (hr) { case MAKE_HRESULT(1, 0x878, 10): result = "Device already allocated"; break; case MAKE_HRESULT(1, 0x878, 30): result = "Control unavailable"; break; case E_INVALIDARG: result = "Invalid parameter"; break; case MAKE_HRESULT(1, 0x878, 50): result = "Invalid call"; break; case E_FAIL: result = "Generic error"; break; case MAKE_HRESULT(1, 0x878, 70): result = "Priority level error"; break; case E_OUTOFMEMORY: result = "Out of memory"; break; case MAKE_HRESULT(1, 0x878, 100): result = "Bad format"; break; case E_NOTIMPL: result = "Unsupported function"; break; case MAKE_HRESULT(1, 0x878, 120): result = "No driver"; break; case MAKE_HRESULT(1, 0x878, 130): result = "Already initialised"; break; case CLASS_E_NOAGGREGATION: result = "No aggregation"; break; case MAKE_HRESULT(1, 0x878, 150): result = "Buffer lost"; break; case MAKE_HRESULT(1, 0x878, 160): result = "Another app has priority"; break; case MAKE_HRESULT(1, 0x878, 170): result = "Uninitialised"; break; case E_NOINTERFACE: result = "No interface"; break; case S_OK: result = "No error"; break; default: return "Unknown error: " + String ((int) hr); } return result; } //============================================================================== #if JUCE_DIRECTSOUND_LOGGING static void logMessage (String message) { message = "DSOUND: " + message; DBG (message); Logger::writeToLog (message); } static void logError (HRESULT hr, int lineNum) { if (FAILED (hr)) { String error ("Error at line "); error << lineNum << ": " << getErrorMessage (hr); logMessage (error); } } #define CATCH JUCE_CATCH_EXCEPTION #define JUCE_DS_LOG(a) DSoundLogging::logMessage(a); #define JUCE_DS_LOG_ERROR(a) DSoundLogging::logError(a, __LINE__); #else #define CATCH JUCE_CATCH_ALL #define JUCE_DS_LOG(a) #define JUCE_DS_LOG_ERROR(a) #endif } //============================================================================== namespace { #define DSOUND_FUNCTION(functionName, params) \ typedef HRESULT (WINAPI *type##functionName) params; \ static type##functionName ds##functionName = nullptr; #define DSOUND_FUNCTION_LOAD(functionName) \ ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \ jassert (ds##functionName != nullptr); typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID); typedef BOOL (CALLBACK *LPDSENUMCALLBACKA) (LPGUID, LPCSTR, LPCSTR, LPVOID); DSOUND_FUNCTION (DirectSoundCreate, (const GUID*, IDirectSound**, LPUNKNOWN)) DSOUND_FUNCTION (DirectSoundCaptureCreate, (const GUID*, IDirectSoundCapture**, LPUNKNOWN)) DSOUND_FUNCTION (DirectSoundEnumerateW, (LPDSENUMCALLBACKW, LPVOID)) DSOUND_FUNCTION (DirectSoundCaptureEnumerateW, (LPDSENUMCALLBACKW, LPVOID)) void initialiseDSoundFunctions() { if (dsDirectSoundCreate == nullptr) { HMODULE h = LoadLibraryA ("dsound.dll"); DSOUND_FUNCTION_LOAD (DirectSoundCreate) DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate) DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW) DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW) } } // the overall size of buffer used is this value x the block size enum { blocksPerOverallBuffer = 16 }; } //============================================================================== class DSoundInternalOutChannel { public: DSoundInternalOutChannel (const String& name_, const GUID& guid_, int rate, int bufferSize, float* left, float* right) : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), pDirectSound (nullptr), pOutputBuffer (nullptr) { } ~DSoundInternalOutChannel() { close(); } void close() { if (pOutputBuffer != nullptr) { JUCE_DS_LOG ("closing output: " + name); HRESULT hr = pOutputBuffer->Stop(); JUCE_DS_LOG_ERROR (hr); (void) hr; pOutputBuffer->Release(); pOutputBuffer = nullptr; } if (pDirectSound != nullptr) { pDirectSound->Release(); pDirectSound = nullptr; } } String open() { JUCE_DS_LOG ("opening output: " + name + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples)); pDirectSound = nullptr; pOutputBuffer = nullptr; writeOffset = 0; String error; HRESULT hr = E_NOINTERFACE; if (dsDirectSoundCreate != nullptr) hr = dsDirectSoundCreate (&guid, &pDirectSound, nullptr); if (SUCCEEDED (hr)) { bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15; totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15; const int numChannels = 2; hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) { IDirectSoundBuffer* pPrimaryBuffer; DSBUFFERDESC primaryDesc = { 0 }; primaryDesc.dwSize = sizeof (DSBUFFERDESC); primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */; primaryDesc.dwBufferBytes = 0; primaryDesc.lpwfxFormat = 0; JUCE_DS_LOG ("co-op level set"); hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) { WAVEFORMATEX wfFormat; wfFormat.wFormatTag = WAVE_FORMAT_PCM; wfFormat.nChannels = (unsigned short) numChannels; wfFormat.nSamplesPerSec = (DWORD) sampleRate; wfFormat.wBitsPerSample = (unsigned short) bitDepth; wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8); wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; wfFormat.cbSize = 0; hr = pPrimaryBuffer->SetFormat (&wfFormat); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) { DSBUFFERDESC secondaryDesc = { 0 }; secondaryDesc.dwSize = sizeof (DSBUFFERDESC); secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */ | 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */; secondaryDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer; secondaryDesc.lpwfxFormat = &wfFormat; hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) { JUCE_DS_LOG ("buffer created"); DWORD dwDataLen; unsigned char* pDSBuffData; hr = pOutputBuffer->Lock (0, (DWORD) totalBytesPerBuffer, (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) { zeromem (pDSBuffData, dwDataLen); hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0); if (SUCCEEDED (hr)) { hr = pOutputBuffer->SetCurrentPosition (0); if (SUCCEEDED (hr)) { hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */); if (SUCCEEDED (hr)) return String::empty; } } } } } } } } error = DSoundLogging::getErrorMessage (hr); close(); return error; } void synchronisePosition() { if (pOutputBuffer != nullptr) { DWORD playCursor; pOutputBuffer->GetCurrentPosition (&playCursor, &writeOffset); } } bool service() { if (pOutputBuffer == 0) return true; DWORD playCursor, writeCursor; for (;;) { HRESULT hr = pOutputBuffer->GetCurrentPosition (&playCursor, &writeCursor); if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST { pOutputBuffer->Restore(); continue; } if (SUCCEEDED (hr)) break; JUCE_DS_LOG_ERROR (hr); jassertfalse; return true; } int playWriteGap = (int) (writeCursor - playCursor); if (playWriteGap < 0) playWriteGap += totalBytesPerBuffer; int bytesEmpty = (int) (playCursor - writeOffset); if (bytesEmpty < 0) bytesEmpty += totalBytesPerBuffer; if (bytesEmpty > (totalBytesPerBuffer - playWriteGap)) { writeOffset = writeCursor; bytesEmpty = totalBytesPerBuffer - playWriteGap; } if (bytesEmpty >= bytesPerBuffer) { int* buf1 = nullptr; int* buf2 = nullptr; DWORD dwSize1 = 0; DWORD dwSize2 = 0; HRESULT hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer, (void**) &buf1, &dwSize1, (void**) &buf2, &dwSize2, 0); if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST { pOutputBuffer->Restore(); hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer, (void**) &buf1, &dwSize1, (void**) &buf2, &dwSize2, 0); } if (SUCCEEDED (hr)) { if (bitDepth == 16) { const float* left = leftBuffer; const float* right = rightBuffer; int samples1 = (int) (dwSize1 >> 2); int samples2 = (int) (dwSize2 >> 2); if (left == nullptr) { for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (0, *right++); for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (0, *right++); } else if (right == nullptr) { for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, 0); for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, 0); } else { for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, *right++); for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, *right++); } } else { jassertfalse; } writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer; pOutputBuffer->Unlock (buf1, dwSize1, buf2, dwSize2); } else { jassertfalse; JUCE_DS_LOG_ERROR (hr); } bytesEmpty -= bytesPerBuffer; return true; } else { return false; } } int bitDepth; bool doneFlag; private: String name; GUID guid; int sampleRate, bufferSizeSamples; float* leftBuffer; float* rightBuffer; IDirectSound* pDirectSound; IDirectSoundBuffer* pOutputBuffer; DWORD writeOffset; int totalBytesPerBuffer, bytesPerBuffer; unsigned int lastPlayCursor; static inline int convertInputValues (const float l, const float r) noexcept { return jlimit (-32768, 32767, roundToInt (32767.0f * r)) << 16 | (0xffff & jlimit (-32768, 32767, roundToInt (32767.0f * l))); } JUCE_DECLARE_NON_COPYABLE (DSoundInternalOutChannel) }; //============================================================================== struct DSoundInternalInChannel { public: DSoundInternalInChannel (const String& name_, const GUID& guid_, int rate, int bufferSize, float* left, float* right) : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), pDirectSound (nullptr), pDirectSoundCapture (nullptr), pInputBuffer (nullptr) { } ~DSoundInternalInChannel() { close(); } void close() { if (pInputBuffer != nullptr) { JUCE_DS_LOG ("closing input: " + name); HRESULT hr = pInputBuffer->Stop(); JUCE_DS_LOG_ERROR (hr); (void) hr; pInputBuffer->Release(); pInputBuffer = nullptr; } if (pDirectSoundCapture != nullptr) { pDirectSoundCapture->Release(); pDirectSoundCapture = nullptr; } if (pDirectSound != nullptr) { pDirectSound->Release(); pDirectSound = nullptr; } } String open() { JUCE_DS_LOG ("opening input: " + name + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples)); pDirectSound = nullptr; pDirectSoundCapture = nullptr; pInputBuffer = nullptr; readOffset = 0; totalBytesPerBuffer = 0; HRESULT hr = dsDirectSoundCaptureCreate != nullptr ? dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, nullptr) : E_NOINTERFACE; if (SUCCEEDED (hr)) { const int numChannels = 2; bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15; totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15; WAVEFORMATEX wfFormat; wfFormat.wFormatTag = WAVE_FORMAT_PCM; wfFormat.nChannels = (unsigned short)numChannels; wfFormat.nSamplesPerSec = (DWORD) sampleRate; wfFormat.wBitsPerSample = (unsigned short) bitDepth; wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; wfFormat.cbSize = 0; DSCBUFFERDESC captureDesc = { 0 }; captureDesc.dwSize = sizeof (DSCBUFFERDESC); captureDesc.dwFlags = 0; captureDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer; captureDesc.lpwfxFormat = &wfFormat; JUCE_DS_LOG ("object created"); hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0); if (SUCCEEDED (hr)) { hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */); if (SUCCEEDED (hr)) return String::empty; } } JUCE_DS_LOG_ERROR (hr); const String error (DSoundLogging::getErrorMessage (hr)); close(); return error; } void synchronisePosition() { if (pInputBuffer != nullptr) { DWORD capturePos; pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*) &readOffset); } } bool service() { if (pInputBuffer == 0) return true; DWORD capturePos, readPos; HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos); JUCE_DS_LOG_ERROR (hr); if (FAILED (hr)) return true; int bytesFilled = (int) (readPos - readOffset); if (bytesFilled < 0) bytesFilled += totalBytesPerBuffer; if (bytesFilled >= bytesPerBuffer) { short* buf1 = nullptr; short* buf2 = nullptr; DWORD dwsize1 = 0; DWORD dwsize2 = 0; HRESULT hr = pInputBuffer->Lock ((DWORD) readOffset, (DWORD) bytesPerBuffer, (void**) &buf1, &dwsize1, (void**) &buf2, &dwsize2, 0); if (SUCCEEDED (hr)) { if (bitDepth == 16) { const float g = 1.0f / 32768.0f; float* destL = leftBuffer; float* destR = rightBuffer; int samples1 = (int) (dwsize1 >> 2); int samples2 = (int) (dwsize2 >> 2); if (destL == nullptr) { for (const short* src = buf1; --samples1 >= 0;) { ++src; *destR++ = *src++ * g; } for (const short* src = buf2; --samples2 >= 0;) { ++src; *destR++ = *src++ * g; } } else if (destR == nullptr) { for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; ++src; } for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; ++src; } } else { for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; } for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; } } } else { jassertfalse; } readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer; pInputBuffer->Unlock (buf1, dwsize1, buf2, dwsize2); } else { JUCE_DS_LOG_ERROR (hr); jassertfalse; } bytesFilled -= bytesPerBuffer; return true; } else { return false; } } unsigned int readOffset; int bytesPerBuffer, totalBytesPerBuffer; int bitDepth; bool doneFlag; private: String name; GUID guid; int sampleRate, bufferSizeSamples; float* leftBuffer; float* rightBuffer; IDirectSound* pDirectSound; IDirectSoundCapture* pDirectSoundCapture; IDirectSoundCaptureBuffer* pInputBuffer; JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel) }; //============================================================================== class DSoundAudioIODevice : public AudioIODevice, public Thread { public: DSoundAudioIODevice (const String& deviceName, const int outputDeviceIndex_, const int inputDeviceIndex_) : AudioIODevice (deviceName, "DirectSound"), Thread ("Juce DSound"), outputDeviceIndex (outputDeviceIndex_), inputDeviceIndex (inputDeviceIndex_), isOpen_ (false), isStarted (false), bufferSizeSamples (0), sampleRate (0.0), callback (nullptr) { if (outputDeviceIndex_ >= 0) { outChannels.add (TRANS("Left")); outChannels.add (TRANS("Right")); } if (inputDeviceIndex_ >= 0) { inChannels.add (TRANS("Left")); inChannels.add (TRANS("Right")); } } ~DSoundAudioIODevice() { close(); } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) override { lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples); isOpen_ = lastError.isEmpty(); return lastError; } void close() override { stop(); if (isOpen_) { closeDevice(); isOpen_ = false; } } bool isOpen() override { return isOpen_ && isThreadRunning(); } int getCurrentBufferSizeSamples() override { return bufferSizeSamples; } double getCurrentSampleRate() override { return sampleRate; } BigInteger getActiveOutputChannels() const override { return enabledOutputs; } BigInteger getActiveInputChannels() const override { return enabledInputs; } int getOutputLatencyInSamples() override { return (int) (getCurrentBufferSizeSamples() * 1.5); } int getInputLatencyInSamples() override { return getOutputLatencyInSamples(); } StringArray getOutputChannelNames() override { return outChannels; } StringArray getInputChannelNames() override { return inChannels; } Array getAvailableSampleRates() override { static const double rates[] = { 44100.0, 48000.0, 88200.0, 96000.0 }; return Array (rates, numElementsInArray (rates)); } Array getAvailableBufferSizes() override { Array r; int n = 64; for (int i = 0; i < 50; ++i) { r.add (n); n += (n < 512) ? 32 : ((n < 1024) ? 64 : ((n < 2048) ? 128 : 256)); } return r; } int getDefaultBufferSize() override { return 2560; } int getCurrentBitDepth() override { int bits = 256; for (int i = inChans.size(); --i >= 0;) bits = jmin (bits, inChans[i]->bitDepth); for (int i = outChans.size(); --i >= 0;) bits = jmin (bits, outChans[i]->bitDepth); if (bits > 32) bits = 16; return bits; } void start (AudioIODeviceCallback* call) override { if (isOpen_ && call != nullptr && ! isStarted) { if (! isThreadRunning()) { // something gone wrong and the thread's stopped.. isOpen_ = false; return; } call->audioDeviceAboutToStart (this); const ScopedLock sl (startStopLock); callback = call; isStarted = true; } } void stop() override { if (isStarted) { AudioIODeviceCallback* const callbackLocal = callback; { const ScopedLock sl (startStopLock); isStarted = false; } if (callbackLocal != nullptr) callbackLocal->audioDeviceStopped(); } } bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); } String getLastError() override { return lastError; } //============================================================================== StringArray inChannels, outChannels; int outputDeviceIndex, inputDeviceIndex; private: bool isOpen_; bool isStarted; String lastError; OwnedArray inChans; OwnedArray outChans; WaitableEvent startEvent; int bufferSizeSamples; double sampleRate; BigInteger enabledInputs, enabledOutputs; AudioSampleBuffer inputBuffers, outputBuffers; AudioIODeviceCallback* callback; CriticalSection startStopLock; String openDevice (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate_, int bufferSizeSamples_); void closeDevice() { isStarted = false; stopThread (5000); inChans.clear(); outChans.clear(); inputBuffers.setSize (1, 1); outputBuffers.setSize (1, 1); } void resync() { if (! threadShouldExit()) { sleep (5); for (int i = 0; i < outChans.size(); ++i) outChans.getUnchecked(i)->synchronisePosition(); for (int i = 0; i < inChans.size(); ++i) inChans.getUnchecked(i)->synchronisePosition(); } } public: void run() override { while (! threadShouldExit()) { if (wait (100)) break; } const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate); const int maxTimeMS = jmax (5, 3 * latencyMs); while (! threadShouldExit()) { int numToDo = 0; uint32 startTime = Time::getMillisecondCounter(); for (int i = inChans.size(); --i >= 0;) { inChans.getUnchecked(i)->doneFlag = false; ++numToDo; } for (int i = outChans.size(); --i >= 0;) { outChans.getUnchecked(i)->doneFlag = false; ++numToDo; } if (numToDo > 0) { const int maxCount = 3; int count = maxCount; for (;;) { for (int i = inChans.size(); --i >= 0;) { DSoundInternalInChannel* const in = inChans.getUnchecked(i); if ((! in->doneFlag) && in->service()) { in->doneFlag = true; --numToDo; } } for (int i = outChans.size(); --i >= 0;) { DSoundInternalOutChannel* const out = outChans.getUnchecked(i); if ((! out->doneFlag) && out->service()) { out->doneFlag = true; --numToDo; } } if (numToDo <= 0) break; if (Time::getMillisecondCounter() > startTime + maxTimeMS) { resync(); break; } if (--count <= 0) { Sleep (1); count = maxCount; } if (threadShouldExit()) return; } } else { sleep (1); } const ScopedLock sl (startStopLock); if (isStarted) { callback->audioDeviceIOCallback (inputBuffers.getArrayOfReadPointers(), inputBuffers.getNumChannels(), outputBuffers.getArrayOfWritePointers(), outputBuffers.getNumChannels(), bufferSizeSamples); } else { outputBuffers.clear(); sleep (1); } } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice) }; //============================================================================== struct DSoundDeviceList { StringArray outputDeviceNames, inputDeviceNames; Array outputGuids, inputGuids; void scan() { outputDeviceNames.clear(); inputDeviceNames.clear(); outputGuids.clear(); inputGuids.clear(); if (dsDirectSoundEnumerateW != 0) { dsDirectSoundEnumerateW (outputEnumProcW, this); dsDirectSoundCaptureEnumerateW (inputEnumProcW, this); } } bool operator!= (const DSoundDeviceList& other) const noexcept { return outputDeviceNames != other.outputDeviceNames || inputDeviceNames != other.inputDeviceNames || outputGuids != other.outputGuids || inputGuids != other.inputGuids; } private: static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array& guids) { desc = desc.trim(); if (desc.isNotEmpty()) { const String origDesc (desc); int n = 2; while (names.contains (desc)) desc = origDesc + " (" + String (n++) + ")"; names.add (desc); guids.add (lpGUID != nullptr ? *lpGUID : GUID()); } return TRUE; } BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); } BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); } static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) { return static_cast (object)->outputEnumProc (lpGUID, description); } static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) { return static_cast (object)->inputEnumProc (lpGUID, description); } }; //============================================================================== String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate_, int bufferSizeSamples_) { closeDevice(); sampleRate = sampleRate_; if (bufferSizeSamples_ <= 0) bufferSizeSamples_ = 960; // use as a default size if none is set. bufferSizeSamples = bufferSizeSamples_ & ~7; DSoundDeviceList dlh; dlh.scan(); enabledInputs = inputChannels; enabledInputs.setRange (inChannels.size(), enabledInputs.getHighestBit() + 1 - inChannels.size(), false); inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples); inputBuffers.clear(); int numIns = 0; for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2) { float* left = enabledInputs[i] ? inputBuffers.getWritePointer (numIns++) : nullptr; float* right = enabledInputs[i + 1] ? inputBuffers.getWritePointer (numIns++) : nullptr; if (left != nullptr || right != nullptr) inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex], dlh.inputGuids [inputDeviceIndex], (int) sampleRate, bufferSizeSamples, left, right)); } enabledOutputs = outputChannels; enabledOutputs.setRange (outChannels.size(), enabledOutputs.getHighestBit() + 1 - outChannels.size(), false); outputBuffers.setSize (jmax (1, enabledOutputs.countNumberOfSetBits()), bufferSizeSamples); outputBuffers.clear(); int numOuts = 0; for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2) { float* left = enabledOutputs[i] ? outputBuffers.getWritePointer (numOuts++) : nullptr; float* right = enabledOutputs[i + 1] ? outputBuffers.getWritePointer (numOuts++) : nullptr; if (left != nullptr || right != nullptr) outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex], dlh.outputGuids [outputDeviceIndex], (int) sampleRate, bufferSizeSamples, left, right)); } String error; // boost our priority while opening the devices to try to get better sync between them const int oldThreadPri = GetThreadPriority (GetCurrentThread()); const DWORD oldProcPri = GetPriorityClass (GetCurrentProcess()); SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS); for (int i = 0; i < outChans.size(); ++i) { error = outChans[i]->open(); if (error.isNotEmpty()) { error = "Error opening " + dlh.outputDeviceNames[i] + ": \"" + error + "\""; break; } } if (error.isEmpty()) { for (int i = 0; i < inChans.size(); ++i) { error = inChans[i]->open(); if (error.isNotEmpty()) { error = "Error opening " + dlh.inputDeviceNames[i] + ": \"" + error + "\""; break; } } } if (error.isEmpty()) { for (int i = 0; i < outChans.size(); ++i) outChans.getUnchecked(i)->synchronisePosition(); for (int i = 0; i < inChans.size(); ++i) inChans.getUnchecked(i)->synchronisePosition(); startThread (9); sleep (10); notify(); } else { JUCE_DS_LOG ("Opening failed: " + error); } SetThreadPriority (GetCurrentThread(), oldThreadPri); SetPriorityClass (GetCurrentProcess(), oldProcPri); return error; } //============================================================================== class DSoundAudioIODeviceType : public AudioIODeviceType, private DeviceChangeDetector { public: DSoundAudioIODeviceType() : AudioIODeviceType ("DirectSound"), DeviceChangeDetector (L"DirectSound"), hasScanned (false) { initialiseDSoundFunctions(); } void scanForDevices() { hasScanned = true; deviceList.scan(); } StringArray getDeviceNames (bool wantInputNames) const { jassert (hasScanned); // need to call scanForDevices() before doing this return wantInputNames ? deviceList.inputDeviceNames : deviceList.outputDeviceNames; } int getDefaultDeviceIndex (bool /*forInput*/) const { jassert (hasScanned); // need to call scanForDevices() before doing this return 0; } int getIndexOfDevice (AudioIODevice* device, bool asInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this if (DSoundAudioIODevice* const d = dynamic_cast (device)) return asInput ? d->inputDeviceIndex : d->outputDeviceIndex; return -1; } bool hasSeparateInputsAndOutputs() const { return true; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { jassert (hasScanned); // need to call scanForDevices() before doing this const int outputIndex = deviceList.outputDeviceNames.indexOf (outputDeviceName); const int inputIndex = deviceList.inputDeviceNames.indexOf (inputDeviceName); if (outputIndex >= 0 || inputIndex >= 0) return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName, outputIndex, inputIndex); return nullptr; } private: DSoundDeviceList deviceList; bool hasScanned; void systemDeviceChanged() override { DSoundDeviceList newList; newList.scan(); if (newList != deviceList) { deviceList = newList; callDeviceChangeListeners(); } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType) }; //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp000066400000000000000000000316231320201440200323260ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class MidiInCollector { public: MidiInCollector (MidiInput* const input_, MidiInputCallback& callback_) : deviceHandle (0), input (input_), callback (callback_), concatenator (4096), isStarted (false), startTime (0) { } ~MidiInCollector() { stop(); if (deviceHandle != 0) { for (int count = 5; --count >= 0;) { if (midiInClose (deviceHandle) == MMSYSERR_NOERROR) break; Sleep (20); } } } //============================================================================== void handleMessage (const uint8* bytes, const uint32 timeStamp) { if (bytes[0] >= 0x80 && isStarted) { concatenator.pushMidiData (bytes, MidiMessage::getMessageLengthFromFirstByte (bytes[0]), convertTimeStamp (timeStamp), input, callback); writeFinishedBlocks(); } } void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) { if (isStarted && hdr->dwBytesRecorded > 0) { concatenator.pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded, convertTimeStamp (timeStamp), input, callback); writeFinishedBlocks(); } } void start() { if (deviceHandle != 0 && ! isStarted) { activeMidiCollectors.addIfNotAlreadyThere (this); for (int i = 0; i < (int) numHeaders; ++i) { headers[i].prepare (deviceHandle); headers[i].write (deviceHandle); } startTime = Time::getMillisecondCounterHiRes(); MMRESULT res = midiInStart (deviceHandle); if (res == MMSYSERR_NOERROR) { concatenator.reset(); isStarted = true; } else { unprepareAllHeaders(); } } } void stop() { if (isStarted) { isStarted = false; midiInReset (deviceHandle); midiInStop (deviceHandle); activeMidiCollectors.removeFirstMatchingValue (this); unprepareAllHeaders(); concatenator.reset(); } } static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR midiMessage, DWORD_PTR timeStamp) { MidiInCollector* const collector = reinterpret_cast (dwInstance); if (activeMidiCollectors.contains (collector)) { if (uMsg == MIM_DATA) collector->handleMessage ((const uint8*) &midiMessage, (uint32) timeStamp); else if (uMsg == MIM_LONGDATA) collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); } } HMIDIIN deviceHandle; private: static Array activeMidiCollectors; MidiInput* input; MidiInputCallback& callback; MidiDataConcatenator concatenator; bool volatile isStarted; double startTime; class MidiHeader { public: MidiHeader() {} void prepare (HMIDIIN deviceHandle) { zerostruct (hdr); hdr.lpData = data; hdr.dwBufferLength = (DWORD) numElementsInArray (data); midiInPrepareHeader (deviceHandle, &hdr, sizeof (hdr)); } void unprepare (HMIDIIN deviceHandle) { if ((hdr.dwFlags & WHDR_DONE) != 0) { int c = 10; while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING) Thread::sleep (20); jassert (c >= 0); } } void write (HMIDIIN deviceHandle) { hdr.dwBytesRecorded = 0; midiInAddBuffer (deviceHandle, &hdr, sizeof (hdr)); } void writeIfFinished (HMIDIIN deviceHandle) { if ((hdr.dwFlags & WHDR_DONE) != 0) write (deviceHandle); } private: MIDIHDR hdr; char data [256]; JUCE_DECLARE_NON_COPYABLE (MidiHeader) }; enum { numHeaders = 32 }; MidiHeader headers [numHeaders]; void writeFinishedBlocks() { for (int i = 0; i < (int) numHeaders; ++i) headers[i].writeIfFinished (deviceHandle); } void unprepareAllHeaders() { for (int i = 0; i < (int) numHeaders; ++i) headers[i].unprepare (deviceHandle); } double convertTimeStamp (uint32 timeStamp) { double t = startTime + timeStamp; const double now = Time::getMillisecondCounterHiRes(); if (t > now) { if (t > now + 2.0) startTime -= 1.0; t = now; } return t * 0.001; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector) }; Array MidiInCollector::activeMidiCollectors; //============================================================================== StringArray MidiInput::getDevices() { StringArray s; const UINT num = midiInGetNumDevs(); for (UINT i = 0; i < num; ++i) { MIDIINCAPS mc = { 0 }; if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) s.add (String (mc.szPname, sizeof (mc.szPname))); } return s; } int MidiInput::getDefaultDeviceIndex() { return 0; } MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const callback) { if (callback == nullptr) return nullptr; UINT deviceId = MIDI_MAPPER; int n = 0; String name; const UINT num = midiInGetNumDevs(); for (UINT i = 0; i < num; ++i) { MIDIINCAPS mc = { 0 }; if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) { if (index == n) { deviceId = i; name = String (mc.szPname, (size_t) numElementsInArray (mc.szPname)); break; } ++n; } } ScopedPointer in (new MidiInput (name)); ScopedPointer collector (new MidiInCollector (in, *callback)); HMIDIIN h; MMRESULT err = midiInOpen (&h, deviceId, (DWORD_PTR) &MidiInCollector::midiInCallback, (DWORD_PTR) (MidiInCollector*) collector, CALLBACK_FUNCTION); if (err == MMSYSERR_NOERROR) { collector->deviceHandle = h; in->internal = collector.release(); return in.release(); } return nullptr; } MidiInput::MidiInput (const String& name_) : name (name_), internal (0) { } MidiInput::~MidiInput() { delete static_cast (internal); } void MidiInput::start() { static_cast (internal)->start(); } void MidiInput::stop() { static_cast (internal)->stop(); } //============================================================================== struct MidiOutHandle { int refCount; UINT deviceId; HMIDIOUT handle; static Array activeHandles; private: JUCE_LEAK_DETECTOR (MidiOutHandle) }; Array MidiOutHandle::activeHandles; //============================================================================== StringArray MidiOutput::getDevices() { StringArray s; const UINT num = midiOutGetNumDevs(); for (UINT i = 0; i < num; ++i) { MIDIOUTCAPS mc = { 0 }; if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) s.add (String (mc.szPname, sizeof (mc.szPname))); } return s; } int MidiOutput::getDefaultDeviceIndex() { const UINT num = midiOutGetNumDevs(); int n = 0; for (UINT i = 0; i < num; ++i) { MIDIOUTCAPS mc = { 0 }; if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) { if ((mc.wTechnology & MOD_MAPPER) != 0) return n; ++n; } } return 0; } MidiOutput* MidiOutput::openDevice (int index) { UINT deviceId = MIDI_MAPPER; const UINT num = midiOutGetNumDevs(); int n = 0; for (UINT i = 0; i < num; ++i) { MIDIOUTCAPS mc = { 0 }; if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) { // use the microsoft sw synth as a default - best not to allow deviceId // to be MIDI_MAPPER, or else device sharing breaks if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase ("microsoft")) deviceId = i; if (index == n) { deviceId = i; break; } ++n; } } for (int i = MidiOutHandle::activeHandles.size(); --i >= 0;) { MidiOutHandle* const han = MidiOutHandle::activeHandles.getUnchecked(i); if (han->deviceId == deviceId) { han->refCount++; MidiOutput* const out = new MidiOutput(); out->internal = han; return out; } } for (int i = 4; --i >= 0;) { HMIDIOUT h = 0; MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL); if (res == MMSYSERR_NOERROR) { MidiOutHandle* const han = new MidiOutHandle(); han->deviceId = deviceId; han->refCount = 1; han->handle = h; MidiOutHandle::activeHandles.add (han); MidiOutput* const out = new MidiOutput(); out->internal = han; return out; } else if (res == MMSYSERR_ALLOCATED) { Sleep (100); } else { break; } } return nullptr; } MidiOutput::~MidiOutput() { stopBackgroundThread(); MidiOutHandle* const h = static_cast (internal); if (MidiOutHandle::activeHandles.contains (h) && --(h->refCount) == 0) { midiOutClose (h->handle); MidiOutHandle::activeHandles.removeFirstMatchingValue (h); delete h; } } void MidiOutput::sendMessageNow (const MidiMessage& message) { const MidiOutHandle* const handle = static_cast (internal); if (message.getRawDataSize() > 3 || message.isSysEx()) { MIDIHDR h = { 0 }; h.lpData = (char*) message.getRawData(); h.dwBytesRecorded = h.dwBufferLength = (DWORD) message.getRawDataSize(); if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR) { MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR)); if (res == MMSYSERR_NOERROR) { while ((h.dwFlags & MHDR_DONE) == 0) Sleep (1); int count = 500; // 1 sec timeout while (--count >= 0) { res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR)); if (res == MIDIERR_STILLPLAYING) Sleep (2); else break; } } } } else { for (int i = 0; i < 50; ++i) { if (midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()) != MIDIERR_NOTREADY) break; Sleep (1); } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp000066400000000000000000001735141320201440200324360ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_WASAPI_LOGGING #define JUCE_WASAPI_LOGGING 0 #endif //============================================================================== namespace WasapiClasses { void logFailure (HRESULT hr) { (void) hr; jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from // a thread which hasn't been initialised with CoInitialize(). #if JUCE_WASAPI_LOGGING if (FAILED (hr)) { const char* m = nullptr; switch (hr) { case E_POINTER: m = "E_POINTER"; break; case E_INVALIDARG: m = "E_INVALIDARG"; break; case E_NOINTERFACE: m = "E_NOINTERFACE"; break; #define JUCE_WASAPI_ERR(desc, n) \ case MAKE_HRESULT(1, 0x889, n): m = #desc; break; JUCE_WASAPI_ERR (AUDCLNT_E_NOT_INITIALIZED, 0x001) JUCE_WASAPI_ERR (AUDCLNT_E_ALREADY_INITIALIZED, 0x002) JUCE_WASAPI_ERR (AUDCLNT_E_WRONG_ENDPOINT_TYPE, 0x003) JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_INVALIDATED, 0x004) JUCE_WASAPI_ERR (AUDCLNT_E_NOT_STOPPED, 0x005) JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_TOO_LARGE, 0x006) JUCE_WASAPI_ERR (AUDCLNT_E_OUT_OF_ORDER, 0x007) JUCE_WASAPI_ERR (AUDCLNT_E_UNSUPPORTED_FORMAT, 0x008) JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_SIZE, 0x009) JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_IN_USE, 0x00a) JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_OPERATION_PENDING, 0x00b) JUCE_WASAPI_ERR (AUDCLNT_E_THREAD_NOT_REGISTERED, 0x00c) JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, 0x00e) JUCE_WASAPI_ERR (AUDCLNT_E_ENDPOINT_CREATE_FAILED, 0x00f) JUCE_WASAPI_ERR (AUDCLNT_E_SERVICE_NOT_RUNNING, 0x010) JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED, 0x011) JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_ONLY, 0x012) JUCE_WASAPI_ERR (AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL, 0x013) JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_SET, 0x014) JUCE_WASAPI_ERR (AUDCLNT_E_INCORRECT_BUFFER_SIZE, 0x015) JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_ERROR, 0x016) JUCE_WASAPI_ERR (AUDCLNT_E_CPUUSAGE_EXCEEDED, 0x017) JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_ERROR, 0x018) JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED, 0x019) JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_DEVICE_PERIOD, 0x020) default: break; } Logger::writeToLog ("WASAPI error: " + (m != nullptr ? String (m) : String::toHexString ((int) hr))); } #endif } #undef check bool check (HRESULT hr) { logFailure (hr); return SUCCEEDED (hr); } //============================================================================== } #if JUCE_MINGW #define JUCE_COMCLASS(name, guid) \ struct name; \ template<> struct UUIDGetter { static CLSID get() { return uuidFromString (guid); } }; \ struct name struct PROPERTYKEY { GUID fmtid; DWORD pid; }; WINOLEAPI PropVariantClear (PROPVARIANT*); #else #define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name #endif #if JUCE_MINGW && defined(KSDATAFORMAT_SUBTYPE_PCM) #undef KSDATAFORMAT_SUBTYPE_PCM #undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT #endif #ifndef KSDATAFORMAT_SUBTYPE_PCM #define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71") #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71") #endif #define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown #define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE enum EDataFlow { eRender = 0, eCapture = (eRender + 1), eAll = (eCapture + 1) }; enum { DEVICE_STATE_ACTIVE = 1, AUDCLNT_BUFFERFLAGS_SILENT = 2 }; JUCE_IUNKNOWNCLASS (IPropertyStore, "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99") { JUCE_COMCALL GetCount (DWORD*) = 0; JUCE_COMCALL GetAt (DWORD, PROPERTYKEY*) = 0; JUCE_COMCALL GetValue (const PROPERTYKEY&, PROPVARIANT*) = 0; JUCE_COMCALL SetValue (const PROPERTYKEY&, const PROPVARIANT&) = 0; JUCE_COMCALL Commit() = 0; }; JUCE_IUNKNOWNCLASS (IMMDevice, "D666063F-1587-4E43-81F1-B948E807363F") { JUCE_COMCALL Activate (REFIID, DWORD, PROPVARIANT*, void**) = 0; JUCE_COMCALL OpenPropertyStore (DWORD, IPropertyStore**) = 0; JUCE_COMCALL GetId (LPWSTR*) = 0; JUCE_COMCALL GetState (DWORD*) = 0; }; JUCE_IUNKNOWNCLASS (IMMEndpoint, "1BE09788-6894-4089-8586-9A2A6C265AC5") { JUCE_COMCALL GetDataFlow (EDataFlow*) = 0; }; struct IMMDeviceCollection : public IUnknown { JUCE_COMCALL GetCount (UINT*) = 0; JUCE_COMCALL Item (UINT, IMMDevice**) = 0; }; enum ERole { eConsole = 0, eMultimedia = (eConsole + 1), eCommunications = (eMultimedia + 1) }; JUCE_IUNKNOWNCLASS (IMMNotificationClient, "7991EEC9-7E89-4D85-8390-6C703CEC60C0") { JUCE_COMCALL OnDeviceStateChanged (LPCWSTR, DWORD) = 0; JUCE_COMCALL OnDeviceAdded (LPCWSTR) = 0; JUCE_COMCALL OnDeviceRemoved (LPCWSTR) = 0; JUCE_COMCALL OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) = 0; JUCE_COMCALL OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) = 0; }; JUCE_IUNKNOWNCLASS (IMMDeviceEnumerator, "A95664D2-9614-4F35-A746-DE8DB63617E6") { JUCE_COMCALL EnumAudioEndpoints (EDataFlow, DWORD, IMMDeviceCollection**) = 0; JUCE_COMCALL GetDefaultAudioEndpoint (EDataFlow, ERole, IMMDevice**) = 0; JUCE_COMCALL GetDevice (LPCWSTR, IMMDevice**) = 0; JUCE_COMCALL RegisterEndpointNotificationCallback (IMMNotificationClient*) = 0; JUCE_COMCALL UnregisterEndpointNotificationCallback (IMMNotificationClient*) = 0; }; JUCE_COMCLASS (MMDeviceEnumerator, "BCDE0395-E52F-467C-8E3D-C4579291692E"); typedef LONGLONG REFERENCE_TIME; enum AVRT_PRIORITY { AVRT_PRIORITY_LOW = -1, AVRT_PRIORITY_NORMAL, AVRT_PRIORITY_HIGH, AVRT_PRIORITY_CRITICAL }; enum AUDCLNT_SHAREMODE { AUDCLNT_SHAREMODE_SHARED, AUDCLNT_SHAREMODE_EXCLUSIVE }; JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") { JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0; JUCE_COMCALL GetBufferSize (UINT32*) = 0; JUCE_COMCALL GetStreamLatency (REFERENCE_TIME*) = 0; JUCE_COMCALL GetCurrentPadding (UINT32*) = 0; JUCE_COMCALL IsFormatSupported (AUDCLNT_SHAREMODE, const WAVEFORMATEX*, WAVEFORMATEX**) = 0; JUCE_COMCALL GetMixFormat (WAVEFORMATEX**) = 0; JUCE_COMCALL GetDevicePeriod (REFERENCE_TIME*, REFERENCE_TIME*) = 0; JUCE_COMCALL Start() = 0; JUCE_COMCALL Stop() = 0; JUCE_COMCALL Reset() = 0; JUCE_COMCALL SetEventHandle (HANDLE) = 0; JUCE_COMCALL GetService (REFIID, void**) = 0; }; JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317") { JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0; JUCE_COMCALL ReleaseBuffer (UINT32) = 0; JUCE_COMCALL GetNextPacketSize (UINT32*) = 0; }; JUCE_IUNKNOWNCLASS (IAudioRenderClient, "F294ACFC-3146-4483-A7BF-ADDCA7C260E2") { JUCE_COMCALL GetBuffer (UINT32, BYTE**) = 0; JUCE_COMCALL ReleaseBuffer (UINT32, DWORD) = 0; }; JUCE_IUNKNOWNCLASS (IAudioEndpointVolume, "5CDF2C82-841E-4546-9722-0CF74078229A") { JUCE_COMCALL RegisterControlChangeNotify (void*) = 0; JUCE_COMCALL UnregisterControlChangeNotify (void*) = 0; JUCE_COMCALL GetChannelCount (UINT*) = 0; JUCE_COMCALL SetMasterVolumeLevel (float, LPCGUID) = 0; JUCE_COMCALL SetMasterVolumeLevelScalar (float, LPCGUID) = 0; JUCE_COMCALL GetMasterVolumeLevel (float*) = 0; JUCE_COMCALL GetMasterVolumeLevelScalar (float*) = 0; JUCE_COMCALL SetChannelVolumeLevel (UINT, float, LPCGUID) = 0; JUCE_COMCALL SetChannelVolumeLevelScalar (UINT, float, LPCGUID) = 0; JUCE_COMCALL GetChannelVolumeLevel (UINT, float*) = 0; JUCE_COMCALL GetChannelVolumeLevelScalar (UINT, float*) = 0; JUCE_COMCALL SetMute (BOOL, LPCGUID) = 0; JUCE_COMCALL GetMute (BOOL*) = 0; JUCE_COMCALL GetVolumeStepInfo (UINT*, UINT*) = 0; JUCE_COMCALL VolumeStepUp (LPCGUID) = 0; JUCE_COMCALL VolumeStepDown (LPCGUID) = 0; JUCE_COMCALL QueryHardwareSupport (DWORD*) = 0; JUCE_COMCALL GetVolumeRange (float*, float*, float*) = 0; }; enum AudioSessionDisconnectReason { DisconnectReasonDeviceRemoval = 0, DisconnectReasonServerShutdown = 1, DisconnectReasonFormatChanged = 2, DisconnectReasonSessionLogoff = 3, DisconnectReasonSessionDisconnected = 4, DisconnectReasonExclusiveModeOverride = 5 }; enum AudioSessionState { AudioSessionStateInactive = 0, AudioSessionStateActive = 1, AudioSessionStateExpired = 2 }; JUCE_IUNKNOWNCLASS (IAudioSessionEvents, "24918ACC-64B3-37C1-8CA9-74A66E9957A8") { JUCE_COMCALL OnDisplayNameChanged (LPCWSTR, LPCGUID) = 0; JUCE_COMCALL OnIconPathChanged (LPCWSTR, LPCGUID) = 0; JUCE_COMCALL OnSimpleVolumeChanged (float, BOOL, LPCGUID) = 0; JUCE_COMCALL OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) = 0; JUCE_COMCALL OnGroupingParamChanged (LPCGUID, LPCGUID) = 0; JUCE_COMCALL OnStateChanged (AudioSessionState) = 0; JUCE_COMCALL OnSessionDisconnected (AudioSessionDisconnectReason) = 0; }; JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD") { JUCE_COMCALL GetState (AudioSessionState*) = 0; JUCE_COMCALL GetDisplayName (LPWSTR*) = 0; JUCE_COMCALL SetDisplayName (LPCWSTR, LPCGUID) = 0; JUCE_COMCALL GetIconPath (LPWSTR*) = 0; JUCE_COMCALL SetIconPath (LPCWSTR, LPCGUID) = 0; JUCE_COMCALL GetGroupingParam (GUID*) = 0; JUCE_COMCALL SetGroupingParam (LPCGUID, LPCGUID) = 0; JUCE_COMCALL RegisterAudioSessionNotification (IAudioSessionEvents*) = 0; JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0; }; #undef JUCE_COMCALL #undef JUCE_COMCLASS #undef JUCE_IUNKNOWNCLASS //============================================================================== namespace WasapiClasses { String getDeviceID (IMMDevice* const device) { String s; WCHAR* deviceId = nullptr; if (check (device->GetId (&deviceId))) { s = String (deviceId); CoTaskMemFree (deviceId); } return s; } EDataFlow getDataFlow (const ComSmartPtr& device) { EDataFlow flow = eRender; ComSmartPtr endPoint; if (check (device.QueryInterface (endPoint))) (void) check (endPoint->GetDataFlow (&flow)); return flow; } int refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) noexcept { return roundToInt (sampleRate * ((double) t) * 0.0000001); } REFERENCE_TIME samplesToRefTime (const int numSamples, const double sampleRate) noexcept { return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5); } void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) noexcept { memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE) : sizeof (WAVEFORMATEX)); } //============================================================================== class WASAPIDeviceBase { public: WASAPIDeviceBase (const ComSmartPtr& d, const bool exclusiveMode) : device (d), sampleRate (0), defaultSampleRate (0), numChannels (0), actualNumChannels (0), minBufferSize (0), defaultBufferSize (0), latencySamples (0), useExclusiveMode (exclusiveMode), actualBufferSize (0), bytesPerSample (0), bytesPerFrame (0), sampleRateHasChanged (false) { clientEvent = CreateEvent (nullptr, false, false, nullptr); ComSmartPtr tempClient (createClient()); if (tempClient == nullptr) return; REFERENCE_TIME defaultPeriod, minPeriod; if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod))) return; WAVEFORMATEX* mixFormat = nullptr; if (! check (tempClient->GetMixFormat (&mixFormat))) return; WAVEFORMATEXTENSIBLE format; copyWavFormat (format, mixFormat); CoTaskMemFree (mixFormat); actualNumChannels = numChannels = format.Format.nChannels; defaultSampleRate = format.Format.nSamplesPerSec; minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate); defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate); mixFormatChannelMask = format.dwChannelMask; rates.addUsingDefaultSort (defaultSampleRate); if (useExclusiveMode && findSupportedFormat (tempClient, defaultSampleRate, format.dwChannelMask, format)) { // Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format) } static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; for (int i = 0; i < numElementsInArray (ratesToTest); ++i) { if (rates.contains (ratesToTest[i])) continue; format.Format.nSamplesPerSec = (DWORD) ratesToTest[i]; format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8); if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*) &format, 0))) if (! rates.contains (ratesToTest[i])) rates.addUsingDefaultSort (ratesToTest[i]); } } virtual ~WASAPIDeviceBase() { device = nullptr; CloseHandle (clientEvent); } bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; } bool openClient (const double newSampleRate, const BigInteger& newChannels, const int bufferSizeSamples) { sampleRate = newSampleRate; channels = newChannels; channels.setRange (actualNumChannels, channels.getHighestBit() + 1 - actualNumChannels, false); numChannels = channels.getHighestBit() + 1; if (numChannels == 0) return true; client = createClient(); if (client != nullptr && tryInitialisingWithBufferSize (bufferSizeSamples)) { sampleRateHasChanged = false; channelMaps.clear(); for (int i = 0; i <= channels.getHighestBit(); ++i) if (channels[i]) channelMaps.add (i); REFERENCE_TIME latency; if (check (client->GetStreamLatency (&latency))) latencySamples = refTimeToSamples (latency, sampleRate); (void) check (client->GetBufferSize (&actualBufferSize)); createSessionEventCallback(); return check (client->SetEventHandle (clientEvent)); } return false; } void closeClient() { if (client != nullptr) client->Stop(); deleteSessionEventCallback(); client = nullptr; ResetEvent (clientEvent); } void deviceSampleRateChanged() { sampleRateHasChanged = true; } //============================================================================== ComSmartPtr device; ComSmartPtr client; double sampleRate, defaultSampleRate; int numChannels, actualNumChannels; int minBufferSize, defaultBufferSize, latencySamples; DWORD mixFormatChannelMask; const bool useExclusiveMode; Array rates; HANDLE clientEvent; BigInteger channels; Array channelMaps; UINT32 actualBufferSize; int bytesPerSample, bytesPerFrame; bool sampleRateHasChanged; virtual void updateFormat (bool isFloat) = 0; private: //============================================================================== class SessionEventCallback : public ComBaseClassHelper { public: SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {} JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; } JUCE_COMRESULT OnIconPathChanged (LPCWSTR, LPCGUID) { return S_OK; } JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; } JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; } JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; } JUCE_COMRESULT OnStateChanged (AudioSessionState) { return S_OK; } JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason) { if (reason == DisconnectReasonFormatChanged) owner.deviceSampleRateChanged(); return S_OK; } private: WASAPIDeviceBase& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback) }; ComSmartPtr audioSessionControl; ComSmartPtr sessionEventCallback; void createSessionEventCallback() { deleteSessionEventCallback(); client->GetService (__uuidof (IAudioSessionControl), (void**) audioSessionControl.resetAndGetPointerAddress()); if (audioSessionControl != nullptr) { sessionEventCallback = new SessionEventCallback (*this); audioSessionControl->RegisterAudioSessionNotification (sessionEventCallback); sessionEventCallback->Release(); // (required because ComBaseClassHelper objects are constructed with a ref count of 1) } } void deleteSessionEventCallback() { if (audioSessionControl != nullptr && sessionEventCallback != nullptr) audioSessionControl->UnregisterAudioSessionNotification (sessionEventCallback); audioSessionControl = nullptr; sessionEventCallback = nullptr; } //============================================================================== ComSmartPtr createClient() { ComSmartPtr client; if (device != nullptr) logFailure (device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER, nullptr, (void**) client.resetAndGetPointerAddress())); return client; } struct AudioSampleFormat { bool useFloat; int bitsPerSampleToTry; int bytesPerSampleContainer; }; bool tryFormat (const AudioSampleFormat sampleFormat, IAudioClient* clientToUse, double sampleRate, DWORD mixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const { zerostruct (format); if (numChannels <= 2 && sampleFormat.bitsPerSampleToTry <= 16) { format.Format.wFormatTag = WAVE_FORMAT_PCM; } else { format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; format.Format.cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX); } format.Format.nSamplesPerSec = (DWORD) sampleRate; format.Format.nChannels = (WORD) numChannels; format.Format.wBitsPerSample = (WORD) (8 * sampleFormat.bytesPerSampleContainer); format.Samples.wValidBitsPerSample = (WORD) (sampleFormat.bitsPerSampleToTry); format.Format.nBlockAlign = (WORD) (format.Format.nChannels * format.Format.wBitsPerSample / 8); format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign); format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; format.dwChannelMask = mixFormatChannelMask; WAVEFORMATEXTENSIBLE* nearestFormat = nullptr; HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*) &format, useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat); logFailure (hr); if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec) { copyWavFormat (format, (const WAVEFORMATEX*) nearestFormat); hr = S_OK; } CoTaskMemFree (nearestFormat); return check (hr); } bool findSupportedFormat (IAudioClient* clientToUse, double sampleRate, DWORD mixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const { static const AudioSampleFormat formats[] = { { true, 32, 4 }, { false, 32, 4 }, { false, 24, 4 }, { false, 24, 3 }, { false, 20, 4 }, { false, 20, 3 }, { false, 16, 2 } }; for (int i = 0; i < numElementsInArray (formats); ++i) if (tryFormat (formats[i], clientToUse, sampleRate, mixFormatChannelMask, format)) return true; return false; } bool tryInitialisingWithBufferSize (const int bufferSizeSamples) { WAVEFORMATEXTENSIBLE format; if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format)) { REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); if (useExclusiveMode && bufferSizeSamples > 0) defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec)); for (;;) { GUID session; HRESULT hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, defaultPeriod, useExclusiveMode ? defaultPeriod : 0, (WAVEFORMATEX*) &format, &session); if (check (hr)) { actualNumChannels = format.Format.nChannels; const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; bytesPerSample = format.Format.wBitsPerSample / 8; bytesPerFrame = format.Format.nBlockAlign; updateFormat (isFloat); return true; } // Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks) if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED break; UINT32 numFrames = 0; if (! check (client->GetBufferSize (&numFrames))) break; // Recreate client client = nullptr; client = createClient(); defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec); } } return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIDeviceBase) }; //============================================================================== class WASAPIInputDevice : public WASAPIDeviceBase { public: WASAPIInputDevice (const ComSmartPtr& d, const bool exclusiveMode) : WASAPIDeviceBase (d, exclusiveMode), reservoir (1, 1) { } ~WASAPIInputDevice() { close(); } bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples) { return openClient (newSampleRate, newChannels, bufferSizeSamples) && (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient), (void**) captureClient.resetAndGetPointerAddress()))); } void close() { closeClient(); captureClient = nullptr; reservoir.reset(); reservoirReadPos = reservoirWritePos = 0; } template void updateFormatWithType (SourceType*) noexcept { typedef AudioData::Pointer NativeType; converter = new AudioData::ConverterInstance, NativeType> (actualNumChannels, 1); } void updateFormat (bool isFloat) override { if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr); else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr); else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr); else updateFormatWithType ((AudioData::Int16*) nullptr); } bool start (const int userBufferSize) { reservoirSize = actualBufferSize + userBufferSize; reservoirMask = nextPowerOfTwo (reservoirSize) - 1; reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true); reservoirReadPos = reservoirWritePos = 0; if (! check (client->Start())) return false; purgeInputBuffers(); return true; } void purgeInputBuffers() { uint8* inputData; UINT32 numSamplesAvailable; DWORD flags; while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr) != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */) captureClient->ReleaseBuffer (numSamplesAvailable); } int getNumSamplesInReservoir() const noexcept { return reservoirWritePos - reservoirReadPos; } void handleDeviceBuffer() { if (numChannels <= 0) return; uint8* inputData; UINT32 numSamplesAvailable; DWORD flags; while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0) { int samplesLeft = (int) numSamplesAvailable; while (samplesLeft > 0) { const int localWrite = reservoirWritePos & reservoirMask; const int samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite); const int samplesToDoBytes = samplesToDo * bytesPerFrame; void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame); if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0) zeromem (reservoirPtr, samplesToDoBytes); else memcpy (reservoirPtr, inputData, samplesToDoBytes); reservoirWritePos += samplesToDo; inputData += samplesToDoBytes; samplesLeft -= samplesToDo; } if (getNumSamplesInReservoir() > reservoirSize) reservoirReadPos = reservoirWritePos - reservoirSize; captureClient->ReleaseBuffer (numSamplesAvailable); } } void copyBuffersFromReservoir (float** destBuffers, int numDestBuffers, int bufferSize) { if ((numChannels <= 0 && bufferSize == 0) || reservoir.getSize() == 0) return; int offset = jmax (0, bufferSize - getNumSamplesInReservoir()); if (offset > 0) { for (int i = 0; i < numDestBuffers; ++i) zeromem (destBuffers[i], offset * sizeof (float)); bufferSize -= offset; reservoirReadPos -= offset / 2; } while (bufferSize > 0) { const int localRead = reservoirReadPos & reservoirMask; const int samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead); if (samplesToDo <= 0) break; const int reservoirOffset = localRead * bytesPerFrame; for (int i = 0; i < numDestBuffers; ++i) converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo); bufferSize -= samplesToDo; offset += samplesToDo; reservoirReadPos += samplesToDo; } } ComSmartPtr captureClient; MemoryBlock reservoir; int reservoirSize, reservoirMask; volatile int reservoirReadPos, reservoirWritePos; ScopedPointer converter; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIInputDevice) }; //============================================================================== class WASAPIOutputDevice : public WASAPIDeviceBase { public: WASAPIOutputDevice (const ComSmartPtr& d, const bool exclusiveMode) : WASAPIDeviceBase (d, exclusiveMode) { } ~WASAPIOutputDevice() { close(); } bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples) { return openClient (newSampleRate, newChannels, bufferSizeSamples) && (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient), (void**) renderClient.resetAndGetPointerAddress()))); } void close() { closeClient(); renderClient = nullptr; } template void updateFormatWithType (DestType*) { typedef AudioData::Pointer NativeType; converter = new AudioData::ConverterInstance > (1, actualNumChannels); } void updateFormat (bool isFloat) override { if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr); else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr); else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr); else updateFormatWithType ((AudioData::Int16*) nullptr); } bool start() { int samplesToDo = getNumSamplesAvailableToCopy(); uint8* outputData; if (check (renderClient->GetBuffer (samplesToDo, &outputData))) renderClient->ReleaseBuffer (samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT); return check (client->Start()); } int getNumSamplesAvailableToCopy() const { if (numChannels <= 0) return 0; if (! useExclusiveMode) { UINT32 padding = 0; if (check (client->GetCurrentPadding (&padding))) return actualBufferSize - (int) padding; } return actualBufferSize; } void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize, WASAPIInputDevice* inputDevice, Thread& thread) { if (numChannels <= 0) return; int offset = 0; while (bufferSize > 0) { // This is needed in order not to drop any input data if the output device endpoint buffer was full if ((! useExclusiveMode) && inputDevice != nullptr && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) inputDevice->handleDeviceBuffer(); int samplesToDo = jmin (getNumSamplesAvailableToCopy(), bufferSize); if (samplesToDo == 0) { // This can ONLY occur in non-exclusive mode if (! thread.threadShouldExit() && WaitForSingleObject (clientEvent, 1000) == WAIT_OBJECT_0) continue; break; } if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) break; uint8* outputData = nullptr; if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData))) { for (int i = 0; i < numSrcBuffers; ++i) converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i] + offset, 0, samplesToDo); renderClient->ReleaseBuffer ((UINT32) samplesToDo, 0); } bufferSize -= samplesToDo; offset += samplesToDo; } } ComSmartPtr renderClient; ScopedPointer converter; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIOutputDevice) }; //============================================================================== class WASAPIAudioIODevice : public AudioIODevice, public Thread, private AsyncUpdater { public: WASAPIAudioIODevice (const String& deviceName, const String& typeName, const String& outputDeviceID, const String& inputDeviceID, const bool exclusiveMode) : AudioIODevice (deviceName, typeName), Thread ("Juce WASAPI"), outputDeviceId (outputDeviceID), inputDeviceId (inputDeviceID), useExclusiveMode (exclusiveMode), isOpen_ (false), isStarted (false), currentBufferSizeSamples (0), currentSampleRate (0), callback (nullptr) { } ~WASAPIAudioIODevice() { close(); } bool initialise() { latencyIn = latencyOut = 0; Array ratesIn, ratesOut; if (createDevices()) { jassert (inputDevice != nullptr || outputDevice != nullptr); if (inputDevice != nullptr && outputDevice != nullptr) { defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate); minBufferSize = jmin (inputDevice->minBufferSize, outputDevice->minBufferSize); defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize); sampleRates = inputDevice->rates; sampleRates.removeValuesNotIn (outputDevice->rates); } else { WASAPIDeviceBase* d = inputDevice != nullptr ? static_cast (inputDevice) : static_cast (outputDevice); defaultSampleRate = d->defaultSampleRate; minBufferSize = d->minBufferSize; defaultBufferSize = d->defaultBufferSize; sampleRates = d->rates; } bufferSizes.addUsingDefaultSort (defaultBufferSize); if (minBufferSize != defaultBufferSize) bufferSizes.addUsingDefaultSort (minBufferSize); int n = 64; for (int i = 0; i < 40; ++i) { if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n)) bufferSizes.addUsingDefaultSort (n); n += (n < 512) ? 32 : (n < 1024 ? 64 : 128); } return true; } return false; } StringArray getOutputChannelNames() override { StringArray outChannels; if (outputDevice != nullptr) for (int i = 1; i <= outputDevice->actualNumChannels; ++i) outChannels.add ("Output channel " + String (i)); return outChannels; } StringArray getInputChannelNames() override { StringArray inChannels; if (inputDevice != nullptr) for (int i = 1; i <= inputDevice->actualNumChannels; ++i) inChannels.add ("Input channel " + String (i)); return inChannels; } Array getAvailableSampleRates() override { return sampleRates; } Array getAvailableBufferSizes() override { return bufferSizes; } int getDefaultBufferSize() override { return defaultBufferSize; } int getCurrentBufferSizeSamples() override { return currentBufferSizeSamples; } double getCurrentSampleRate() override { return currentSampleRate; } int getCurrentBitDepth() override { return 32; } int getOutputLatencyInSamples() override { return latencyOut; } int getInputLatencyInSamples() override { return latencyIn; } BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); } BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); } String getLastError() override { return lastError; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) override { close(); lastError.clear(); if (sampleRates.size() == 0 && inputDevice != nullptr && outputDevice != nullptr) { lastError = TRANS("The input and output devices don't share a common sample rate!"); return lastError; } currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize); currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate; lastKnownInputChannels = inputChannels; lastKnownOutputChannels = outputChannels; if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels, bufferSizeSamples)) { lastError = TRANS("Couldn't open the input device!"); return lastError; } if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels, bufferSizeSamples)) { close(); lastError = TRANS("Couldn't open the output device!"); return lastError; } if (useExclusiveMode) { // This is to make sure that the callback uses actualBufferSize in case of exclusive mode if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize) { close(); lastError = TRANS("Couldn't open the output device (buffer size mismatch)"); return lastError; } currentBufferSizeSamples = outputDevice != nullptr ? outputDevice->actualBufferSize : inputDevice->actualBufferSize; } if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent); if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent); startThread (8); Thread::sleep (5); if (inputDevice != nullptr && inputDevice->client != nullptr) { latencyIn = (int) (inputDevice->latencySamples + currentBufferSizeSamples); if (! inputDevice->start (currentBufferSizeSamples)) { close(); lastError = TRANS("Couldn't start the input device!"); return lastError; } } if (outputDevice != nullptr && outputDevice->client != nullptr) { latencyOut = (int) (outputDevice->latencySamples + currentBufferSizeSamples); if (! outputDevice->start()) { close(); lastError = TRANS("Couldn't start the output device!"); return lastError; } } isOpen_ = true; return lastError; } void close() override { stop(); signalThreadShouldExit(); if (inputDevice != nullptr) SetEvent (inputDevice->clientEvent); if (outputDevice != nullptr) SetEvent (outputDevice->clientEvent); stopThread (5000); if (inputDevice != nullptr) inputDevice->close(); if (outputDevice != nullptr) outputDevice->close(); isOpen_ = false; } bool isOpen() override { return isOpen_ && isThreadRunning(); } bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); } void start (AudioIODeviceCallback* call) override { if (isOpen_ && call != nullptr && ! isStarted) { if (! isThreadRunning()) { // something's gone wrong and the thread's stopped.. isOpen_ = false; return; } call->audioDeviceAboutToStart (this); const ScopedLock sl (startStopLock); callback = call; isStarted = true; } } void stop() override { if (isStarted) { AudioIODeviceCallback* const callbackLocal = callback; { const ScopedLock sl (startStopLock); isStarted = false; } if (callbackLocal != nullptr) callbackLocal->audioDeviceStopped(); } } void setMMThreadPriority() { DynamicLibrary dll ("avrt.dll"); JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadCharacteristicsW, avSetMmThreadCharacteristics, HANDLE, (LPCWSTR, LPDWORD)) JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, (HANDLE, AVRT_PRIORITY)) if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0) { DWORD dummy = 0; HANDLE h = avSetMmThreadCharacteristics (L"Pro Audio", &dummy); if (h != 0) avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL); } } void run() override { setMMThreadPriority(); const int bufferSize = currentBufferSizeSamples; const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits(); const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits(); bool sampleRateHasChanged = false; AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32); AudioSampleBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32); float** const inputBuffers = ins.getArrayOfWritePointers(); float** const outputBuffers = outs.getArrayOfWritePointers(); ins.clear(); outs.clear(); while (! threadShouldExit()) { if (inputDevice != nullptr) { if (outputDevice == nullptr) { if (WaitForSingleObject (inputDevice->clientEvent, 1000) == WAIT_TIMEOUT) break; inputDevice->handleDeviceBuffer(); if (inputDevice->getNumSamplesInReservoir() < bufferSize) continue; } else { if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) inputDevice->handleDeviceBuffer(); } inputDevice->copyBuffersFromReservoir (inputBuffers, numInputBuffers, bufferSize); if (inputDevice->sampleRateHasChanged) { sampleRateHasChanged = true; sampleRateChangedByOutput = false; } } { const ScopedTryLock sl (startStopLock); if (sl.isLocked() && isStarted) callback->audioDeviceIOCallback (const_cast (inputBuffers), numInputBuffers, outputBuffers, numOutputBuffers, bufferSize); else outs.clear(); } if (outputDevice != nullptr) { // Note that this function is handed the input device so it can check for the event and make sure // the input reservoir is filled up correctly even when bufferSize > device actualBufferSize outputDevice->copyBuffers (const_cast (outputBuffers), numOutputBuffers, bufferSize, inputDevice, *this); if (outputDevice->sampleRateHasChanged) { sampleRateHasChanged = true; sampleRateChangedByOutput = true; } } if (sampleRateHasChanged) { triggerAsyncUpdate(); break; // Quit the thread... will restart it later! } } } //============================================================================== String outputDeviceId, inputDeviceId; String lastError; private: // Device stats... ScopedPointer inputDevice; ScopedPointer outputDevice; const bool useExclusiveMode; double defaultSampleRate; int minBufferSize, defaultBufferSize; int latencyIn, latencyOut; Array sampleRates; Array bufferSizes; // Active state... bool isOpen_, isStarted; int currentBufferSizeSamples; double currentSampleRate; bool sampleRateChangedByOutput; AudioIODeviceCallback* callback; CriticalSection startStopLock; BigInteger lastKnownInputChannels, lastKnownOutputChannels; //============================================================================== bool createDevices() { ComSmartPtr enumerator; if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) return false; ComSmartPtr deviceCollection; if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress()))) return false; UINT32 numDevices = 0; if (! check (deviceCollection->GetCount (&numDevices))) return false; for (UINT32 i = 0; i < numDevices; ++i) { ComSmartPtr device; if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) continue; const String deviceId (getDeviceID (device)); if (deviceId.isEmpty()) continue; const EDataFlow flow = getDataFlow (device); if (deviceId == inputDeviceId && flow == eCapture) inputDevice = new WASAPIInputDevice (device, useExclusiveMode); else if (deviceId == outputDeviceId && flow == eRender) outputDevice = new WASAPIOutputDevice (device, useExclusiveMode); } return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk())) && (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk())); } //============================================================================== void handleAsyncUpdate() override { stop(); outputDevice = nullptr; inputDevice = nullptr; initialise(); open (lastKnownInputChannels, lastKnownOutputChannels, getChangedSampleRate(), currentBufferSizeSamples); start (callback); } double getChangedSampleRate() const { if (outputDevice != nullptr && sampleRateChangedByOutput) return outputDevice->defaultSampleRate; if (inputDevice != nullptr && ! sampleRateChangedByOutput) return inputDevice->defaultSampleRate; return 0.0; } //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice) }; //============================================================================== class WASAPIAudioIODeviceType : public AudioIODeviceType, private DeviceChangeDetector { public: WASAPIAudioIODeviceType (bool exclusive) : AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"), DeviceChangeDetector (L"Windows Audio"), exclusiveMode (exclusive), hasScanned (false) { } ~WASAPIAudioIODeviceType() { if (notifyClient != nullptr) enumerator->UnregisterEndpointNotificationCallback (notifyClient); } //============================================================================== void scanForDevices() { hasScanned = true; outputDeviceNames.clear(); inputDeviceNames.clear(); outputDeviceIds.clear(); inputDeviceIds.clear(); scan (outputDeviceNames, inputDeviceNames, outputDeviceIds, inputDeviceIds); } StringArray getDeviceNames (bool wantInputNames) const { jassert (hasScanned); // need to call scanForDevices() before doing this return wantInputNames ? inputDeviceNames : outputDeviceNames; } int getDefaultDeviceIndex (bool /*forInput*/) const { jassert (hasScanned); // need to call scanForDevices() before doing this return 0; } int getIndexOfDevice (AudioIODevice* device, bool asInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this if (WASAPIAudioIODevice* const d = dynamic_cast (device)) return asInput ? inputDeviceIds.indexOf (d->inputDeviceId) : outputDeviceIds.indexOf (d->outputDeviceId); return -1; } bool hasSeparateInputsAndOutputs() const { return true; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { jassert (hasScanned); // need to call scanForDevices() before doing this ScopedPointer device; const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); if (outputIndex >= 0 || inputIndex >= 0) { device = new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName, getTypeName(), outputDeviceIds [outputIndex], inputDeviceIds [inputIndex], exclusiveMode); if (! device->initialise()) device = nullptr; } return device.release(); } //============================================================================== StringArray outputDeviceNames, outputDeviceIds; StringArray inputDeviceNames, inputDeviceIds; private: bool exclusiveMode, hasScanned; ComSmartPtr enumerator; //============================================================================== class ChangeNotificationClient : public ComBaseClassHelper { public: ChangeNotificationClient (WASAPIAudioIODeviceType& d) : ComBaseClassHelper (0), device (d) {} HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); } HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); } HRESULT STDMETHODCALLTYPE OnDeviceStateChanged (LPCWSTR, DWORD) { return notify(); } HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); } HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); } private: WASAPIAudioIODeviceType& device; HRESULT notify() { device.triggerAsyncDeviceChangeCallback(); return S_OK; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeNotificationClient) }; ComSmartPtr notifyClient; //============================================================================== static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture) { String s; IMMDevice* dev = nullptr; if (check (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender, eMultimedia, &dev))) { WCHAR* deviceId = nullptr; if (check (dev->GetId (&deviceId))) { s = deviceId; CoTaskMemFree (deviceId); } dev->Release(); } return s; } //============================================================================== void scan (StringArray& outputDeviceNames, StringArray& inputDeviceNames, StringArray& outputDeviceIds, StringArray& inputDeviceIds) { // Init COM on thread (WASAPI won't work with libopenshot-audio without this line) CoInitialize(0); if (enumerator == nullptr) { if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) return; notifyClient = new ChangeNotificationClient (*this); enumerator->RegisterEndpointNotificationCallback (notifyClient); } const String defaultRenderer (getDefaultEndpoint (enumerator, false)); const String defaultCapture (getDefaultEndpoint (enumerator, true)); ComSmartPtr deviceCollection; UINT32 numDevices = 0; if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) && check (deviceCollection->GetCount (&numDevices)))) return; for (UINT32 i = 0; i < numDevices; ++i) { ComSmartPtr device; if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) continue; DWORD state = 0; if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE)) continue; const String deviceId (getDeviceID (device)); String name; { ComSmartPtr properties; if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress()))) continue; PROPVARIANT value; zerostruct (value); const PROPERTYKEY PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 }; if (check (properties->GetValue (PKEY_Device_FriendlyName, &value))) name = value.pwszVal; PropVariantClear (&value); } const EDataFlow flow = getDataFlow (device); if (flow == eRender) { const int index = (deviceId == defaultRenderer) ? 0 : -1; outputDeviceIds.insert (index, deviceId); outputDeviceNames.insert (index, name); } else if (flow == eCapture) { const int index = (deviceId == defaultCapture) ? 0 : -1; inputDeviceIds.insert (index, deviceId); inputDeviceNames.insert (index, name); } } inputDeviceNames.appendNumbersToDuplicates (false, false); outputDeviceNames.appendNumbersToDuplicates (false, false); } //============================================================================== void systemDeviceChanged() override { StringArray newOutNames, newInNames, newOutIds, newInIds; scan (newOutNames, newInNames, newOutIds, newInIds); if (newOutNames != outputDeviceNames || newInNames != inputDeviceNames || newOutIds != outputDeviceIds || newInIds != inputDeviceIds) { hasScanned = true; outputDeviceNames = newOutNames; inputDeviceNames = newInNames; outputDeviceIds = newOutIds; inputDeviceIds = newInIds; } callDeviceChangeListeners(); } //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType) }; //============================================================================== struct MMDeviceMasterVolume { MMDeviceMasterVolume() { ComSmartPtr enumerator; if (check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) { ComSmartPtr device; if (check (enumerator->GetDefaultAudioEndpoint (eRender, eConsole, device.resetAndGetPointerAddress()))) check (device->Activate (__uuidof (IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, (void**) endpointVolume.resetAndGetPointerAddress())); } } float getGain() const { float vol = 0.0f; if (endpointVolume != nullptr) check (endpointVolume->GetMasterVolumeLevelScalar (&vol)); return vol; } bool setGain (float newGain) const { return endpointVolume != nullptr && check (endpointVolume->SetMasterVolumeLevelScalar (jlimit (0.0f, 1.0f, newGain), nullptr)); } bool isMuted() const { BOOL mute = 0; return endpointVolume != nullptr && check (endpointVolume->GetMute (&mute)) && mute != 0; } bool setMuted (bool shouldMute) const { return endpointVolume != nullptr && check (endpointVolume->SetMute (shouldMute, nullptr)); } ComSmartPtr endpointVolume; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MMDeviceMasterVolume) }; } //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode) { #if ! JUCE_WASAPI_EXCLUSIVE if (exclusiveMode) return nullptr; #endif return SystemStats::getOperatingSystemType() >= SystemStats::WinVista ? new WasapiClasses::WASAPIAudioIODeviceType (exclusiveMode) : nullptr; } //============================================================================== #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); } bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return WasapiClasses::MMDeviceMasterVolume().setGain (gain); } bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return WasapiClasses::MMDeviceMasterVolume().isMuted(); } bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return WasapiClasses::MMDeviceMasterVolume().setMuted (mute); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/sources/000077500000000000000000000000001320201440200271005ustar00rootroot00000000000000juce_AudioSourcePlayer.cpp000066400000000000000000000135511320201440200341370ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioSourcePlayer::AudioSourcePlayer() : source (nullptr), sampleRate (0), bufferSize (0), lastGain (1.0f), gain (1.0f) { } AudioSourcePlayer::~AudioSourcePlayer() { setSource (nullptr); } void AudioSourcePlayer::setSource (AudioSource* newSource) { if (source != newSource) { AudioSource* const oldSource = source; if (newSource != nullptr && bufferSize > 0 && sampleRate > 0) newSource->prepareToPlay (bufferSize, sampleRate); { const ScopedLock sl (readLock); source = newSource; } if (oldSource != nullptr) oldSource->releaseResources(); } } void AudioSourcePlayer::setGain (const float newGain) noexcept { gain = newGain; } void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, int totalNumInputChannels, float** outputChannelData, int totalNumOutputChannels, int numSamples) { // these should have been prepared by audioDeviceAboutToStart()... jassert (sampleRate > 0 && bufferSize > 0); const ScopedLock sl (readLock); if (source != nullptr) { int numActiveChans = 0, numInputs = 0, numOutputs = 0; // messy stuff needed to compact the channels down into an array // of non-zero pointers.. for (int i = 0; i < totalNumInputChannels; ++i) { if (inputChannelData[i] != nullptr) { inputChans [numInputs++] = inputChannelData[i]; if (numInputs >= numElementsInArray (inputChans)) break; } } for (int i = 0; i < totalNumOutputChannels; ++i) { if (outputChannelData[i] != nullptr) { outputChans [numOutputs++] = outputChannelData[i]; if (numOutputs >= numElementsInArray (outputChans)) break; } } if (numInputs > numOutputs) { // if there aren't enough output channels for the number of // inputs, we need to create some temporary extra ones (can't // use the input data in case it gets written to) tempBuffer.setSize (numInputs - numOutputs, numSamples, false, false, true); for (int i = 0; i < numOutputs; ++i) { channels[numActiveChans] = outputChans[i]; memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); ++numActiveChans; } for (int i = numOutputs; i < numInputs; ++i) { channels[numActiveChans] = tempBuffer.getWritePointer (i - numOutputs); memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); ++numActiveChans; } } else { for (int i = 0; i < numInputs; ++i) { channels[numActiveChans] = outputChans[i]; memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); ++numActiveChans; } for (int i = numInputs; i < numOutputs; ++i) { channels[numActiveChans] = outputChans[i]; zeromem (channels[numActiveChans], sizeof (float) * (size_t) numSamples); ++numActiveChans; } } AudioSampleBuffer buffer (channels, numActiveChans, numSamples); AudioSourceChannelInfo info (&buffer, 0, numSamples); source->getNextAudioBlock (info); for (int i = info.buffer->getNumChannels(); --i >= 0;) buffer.applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain); lastGain = gain; } else { for (int i = 0; i < totalNumOutputChannels; ++i) if (outputChannelData[i] != nullptr) zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples); } } void AudioSourcePlayer::audioDeviceAboutToStart (AudioIODevice* device) { prepareToPlay (device->getCurrentSampleRate(), device->getCurrentBufferSizeSamples()); } void AudioSourcePlayer::prepareToPlay (double newSampleRate, int newBufferSize) { sampleRate = newSampleRate; bufferSize = newBufferSize; zeromem (channels, sizeof (channels)); if (source != nullptr) source->prepareToPlay (bufferSize, sampleRate); } void AudioSourcePlayer::audioDeviceStopped() { if (source != nullptr) source->releaseResources(); sampleRate = 0.0; bufferSize = 0; tempBuffer.setSize (2, 8); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h000066400000000000000000000104151320201440200336570ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOSOURCEPLAYER_H_INCLUDED #define JUCE_AUDIOSOURCEPLAYER_H_INCLUDED //============================================================================== /** Wrapper class to continuously stream audio from an audio source to an AudioIODevice. This object acts as an AudioIODeviceCallback, so can be attached to an output device, and will stream audio from an AudioSource. */ class JUCE_API AudioSourcePlayer : public AudioIODeviceCallback { public: //============================================================================== /** Creates an empty AudioSourcePlayer. */ AudioSourcePlayer(); /** Destructor. Make sure this object isn't still being used by an AudioIODevice before deleting it! */ virtual ~AudioSourcePlayer(); //============================================================================== /** Changes the current audio source to play from. If the source passed in is already being used, this method will do nothing. If the source is not null, its prepareToPlay() method will be called before it starts being used for playback. If there's another source currently playing, its releaseResources() method will be called after it has been swapped for the new one. @param newSource the new source to use - this will NOT be deleted by this object when no longer needed, so it's the caller's responsibility to manage it. */ void setSource (AudioSource* newSource); /** Returns the source that's playing. May return nullptr if there's no source. */ AudioSource* getCurrentSource() const noexcept { return source; } /** Sets a gain to apply to the audio data. @see getGain */ void setGain (float newGain) noexcept; /** Returns the current gain. @see setGain */ float getGain() const noexcept { return gain; } //============================================================================== /** Implementation of the AudioIODeviceCallback method. */ void audioDeviceIOCallback (const float** inputChannelData, int totalNumInputChannels, float** outputChannelData, int totalNumOutputChannels, int numSamples) override; /** Implementation of the AudioIODeviceCallback method. */ void audioDeviceAboutToStart (AudioIODevice* device) override; /** Implementation of the AudioIODeviceCallback method. */ void audioDeviceStopped() override; /** An alternative method for initialising the source without an AudioIODevice. */ void prepareToPlay (double sampleRate, int blockSize); private: //============================================================================== CriticalSection readLock; AudioSource* source; double sampleRate; int bufferSize; float* channels [128]; float* outputChans [128]; const float* inputChans [128]; AudioSampleBuffer tempBuffer; float lastGain, gain; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourcePlayer) }; #endif // JUCE_AUDIOSOURCEPLAYER_H_INCLUDED juce_AudioTransportSource.cpp000066400000000000000000000207661320201440200347050ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioTransportSource::AudioTransportSource() : source (nullptr), resamplerSource (nullptr), bufferingSource (nullptr), positionableSource (nullptr), masterSource (nullptr), gain (1.0f), lastGain (1.0f), playing (false), stopped (true), sampleRate (44100.0), sourceSampleRate (0.0), blockSize (128), readAheadBufferSize (0), isPrepared (false), inputStreamEOF (false) { } AudioTransportSource::~AudioTransportSource() { setSource (nullptr); releaseMasterResources(); } void AudioTransportSource::setSource (PositionableAudioSource* const newSource, int readAheadSize, TimeSliceThread* readAheadThread, double sourceSampleRateToCorrectFor, int maxNumChannels) { if (source == newSource) { if (source == nullptr) return; setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly } readAheadBufferSize = readAheadSize; sourceSampleRate = sourceSampleRateToCorrectFor; ResamplingAudioSource* newResamplerSource = nullptr; BufferingAudioSource* newBufferingSource = nullptr; PositionableAudioSource* newPositionableSource = nullptr; AudioSource* newMasterSource = nullptr; ScopedPointer oldResamplerSource (resamplerSource); ScopedPointer oldBufferingSource (bufferingSource); AudioSource* oldMasterSource = masterSource; if (newSource != nullptr) { newPositionableSource = newSource; if (readAheadSize > 0) { // If you want to use a read-ahead buffer, you must also provide a TimeSliceThread // for it to use! jassert (readAheadThread != nullptr); newPositionableSource = newBufferingSource = new BufferingAudioSource (newPositionableSource, *readAheadThread, false, readAheadSize, maxNumChannels); } newPositionableSource->setNextReadPosition (0); if (sourceSampleRateToCorrectFor > 0) newMasterSource = newResamplerSource = new ResamplingAudioSource (newPositionableSource, false, maxNumChannels); else newMasterSource = newPositionableSource; if (isPrepared) { if (newResamplerSource != nullptr && sourceSampleRate > 0 && sampleRate > 0) newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); newMasterSource->prepareToPlay (blockSize, sampleRate); } } { const ScopedLock sl (callbackLock); source = newSource; resamplerSource = newResamplerSource; bufferingSource = newBufferingSource; masterSource = newMasterSource; positionableSource = newPositionableSource; inputStreamEOF = false; playing = false; } if (oldMasterSource != nullptr) oldMasterSource->releaseResources(); } void AudioTransportSource::start() { if ((! playing) && masterSource != nullptr) { { const ScopedLock sl (callbackLock); playing = true; stopped = false; inputStreamEOF = false; } sendChangeMessage(); } } void AudioTransportSource::stop() { if (playing) { { const ScopedLock sl (callbackLock); playing = false; } int n = 500; while (--n >= 0 && ! stopped) Thread::sleep (2); sendChangeMessage(); } } void AudioTransportSource::setPosition (double newPosition) { if (sampleRate > 0.0) setNextReadPosition ((int64) (newPosition * sampleRate)); } double AudioTransportSource::getCurrentPosition() const { if (sampleRate > 0.0) return getNextReadPosition() / sampleRate; return 0.0; } double AudioTransportSource::getLengthInSeconds() const { if (sampleRate > 0.0) return getTotalLength() / sampleRate; return 0.0; } void AudioTransportSource::setNextReadPosition (int64 newPosition) { if (positionableSource != nullptr) { if (sampleRate > 0 && sourceSampleRate > 0) newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); positionableSource->setNextReadPosition (newPosition); if (resamplerSource != nullptr) resamplerSource->flushBuffers(); inputStreamEOF = false; } } int64 AudioTransportSource::getNextReadPosition() const { if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; return (int64) (positionableSource->getNextReadPosition() * ratio); } return 0; } int64 AudioTransportSource::getTotalLength() const { const ScopedLock sl (callbackLock); if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; return (int64) (positionableSource->getTotalLength() * ratio); } return 0; } bool AudioTransportSource::isLooping() const { const ScopedLock sl (callbackLock); return positionableSource != nullptr && positionableSource->isLooping(); } void AudioTransportSource::setGain (const float newGain) noexcept { gain = newGain; } void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate) { const ScopedLock sl (callbackLock); sampleRate = newSampleRate; blockSize = samplesPerBlockExpected; if (masterSource != nullptr) masterSource->prepareToPlay (samplesPerBlockExpected, sampleRate); if (resamplerSource != nullptr && sourceSampleRate > 0) resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); inputStreamEOF = false; isPrepared = true; } void AudioTransportSource::releaseMasterResources() { const ScopedLock sl (callbackLock); if (masterSource != nullptr) masterSource->releaseResources(); isPrepared = false; } void AudioTransportSource::releaseResources() { releaseMasterResources(); } void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { const ScopedLock sl (callbackLock); if (masterSource != nullptr && ! stopped) { masterSource->getNextAudioBlock (info); if (! playing) { // just stopped playing, so fade out the last block.. for (int i = info.buffer->getNumChannels(); --i >= 0;) info.buffer->applyGainRamp (i, info.startSample, jmin (256, info.numSamples), 1.0f, 0.0f); if (info.numSamples > 256) info.buffer->clear (info.startSample + 256, info.numSamples - 256); } if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 && ! positionableSource->isLooping()) { playing = false; inputStreamEOF = true; sendChangeMessage(); } stopped = ! playing; for (int i = info.buffer->getNumChannels(); --i >= 0;) info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain); } else { info.clearActiveBufferRegion(); stopped = true; } lastGain = gain; } juce_AudioTransportSource.h000066400000000000000000000170731320201440200343470ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_devices/sources/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED #define JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED //============================================================================== /** An AudioSource that takes a PositionableAudioSource and allows it to be played, stopped, started, etc. This can also be told use a buffer and background thread to read ahead, and if can correct for different sample-rates. You may want to use one of these along with an AudioSourcePlayer and AudioIODevice to control playback of an audio file. @see AudioSource, AudioSourcePlayer */ class JUCE_API AudioTransportSource : public PositionableAudioSource, public ChangeBroadcaster { public: //============================================================================== /** Creates an AudioTransportSource. After creating one of these, use the setSource() method to select an input source. */ AudioTransportSource(); /** Destructor. */ ~AudioTransportSource(); //============================================================================== /** Sets the reader that is being used as the input source. This will stop playback, reset the position to 0 and change to the new reader. The source passed in will not be deleted by this object, so must be managed by the caller. @param newSource the new input source to use. This may be zero @param readAheadBufferSize a size of buffer to use for reading ahead. If this is zero, no reading ahead will be done; if it's greater than zero, a BufferingAudioSource will be used to do the reading-ahead. If you set a non-zero value here, you'll also need to set the readAheadThread parameter. @param readAheadThread if you set readAheadBufferSize to a non-zero value, then you'll also need to supply this TimeSliceThread object for the background reader to use. The thread object must not be deleted while the AudioTransport source is still using it. @param sourceSampleRateToCorrectFor if this is non-zero, it specifies the sample rate of the source, and playback will be sample-rate adjusted to maintain playback at the correct pitch. If this is 0, no sample-rate adjustment will be performed @param maxNumChannels the maximum number of channels that may need to be played */ void setSource (PositionableAudioSource* newSource, int readAheadBufferSize = 0, TimeSliceThread* readAheadThread = nullptr, double sourceSampleRateToCorrectFor = 0.0, int maxNumChannels = 2); //============================================================================== /** Changes the current playback position in the source stream. The next time the getNextAudioBlock() method is called, this is the time from which it'll read data. @see getPosition */ void setPosition (double newPosition); /** Returns the position that the next data block will be read from This is a time in seconds. */ double getCurrentPosition() const; /** Returns the stream's length in seconds. */ double getLengthInSeconds() const; /** Returns true if the player has stopped because its input stream ran out of data. */ bool hasStreamFinished() const noexcept { return inputStreamEOF; } //============================================================================== /** Starts playing (if a source has been selected). If it starts playing, this will send a message to any ChangeListeners that are registered with this object. */ void start(); /** Stops playing. If it's actually playing, this will send a message to any ChangeListeners that are registered with this object. */ void stop(); /** Returns true if it's currently playing. */ bool isPlaying() const noexcept { return playing; } //============================================================================== /** Changes the gain to apply to the output. @param newGain a factor by which to multiply the outgoing samples, so 1.0 = 0dB, 0.5 = -6dB, 2.0 = 6dB, etc. */ void setGain (float newGain) noexcept; /** Returns the current gain setting. @see setGain */ float getGain() const noexcept { return gain; } //============================================================================== /** Implementation of the AudioSource method. */ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ void releaseResources() override; /** Implementation of the AudioSource method. */ void getNextAudioBlock (const AudioSourceChannelInfo&) override; //============================================================================== /** Implements the PositionableAudioSource method. */ void setNextReadPosition (int64 newPosition) override; /** Implements the PositionableAudioSource method. */ int64 getNextReadPosition() const override; /** Implements the PositionableAudioSource method. */ int64 getTotalLength() const override; /** Implements the PositionableAudioSource method. */ bool isLooping() const override; private: //============================================================================== PositionableAudioSource* source; ResamplingAudioSource* resamplerSource; BufferingAudioSource* bufferingSource; PositionableAudioSource* positionableSource; AudioSource* masterSource; CriticalSection callbackLock; float volatile gain, lastGain; bool volatile playing, stopped; double sampleRate, sourceSampleRate; int blockSize, readAheadBufferSize; bool volatile isPrepared, inputStreamEOF; void releaseMasterResources(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioTransportSource) }; #endif // JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/000077500000000000000000000000001320201440200254465ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/000077500000000000000000000000001320201440200267065ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp000066400000000000000000001253041320201440200334250ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ static const char* const aiffFormatName = "AIFF file"; //============================================================================== const char* const AiffAudioFormat::appleOneShot = "apple one shot"; const char* const AiffAudioFormat::appleRootSet = "apple root set"; const char* const AiffAudioFormat::appleRootNote = "apple root note"; const char* const AiffAudioFormat::appleBeats = "apple beats"; const char* const AiffAudioFormat::appleDenominator = "apple denominator"; const char* const AiffAudioFormat::appleNumerator = "apple numerator"; const char* const AiffAudioFormat::appleTag = "apple tag"; const char* const AiffAudioFormat::appleKey = "apple key"; //============================================================================== namespace AiffFileHelpers { inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); } #if JUCE_MSVC #pragma pack (push, 1) #endif //============================================================================== struct InstChunk { struct Loop { uint16 type; // these are different in AIFF and WAV uint16 startIdentifier; uint16 endIdentifier; } JUCE_PACKED; int8 baseNote; int8 detune; int8 lowNote; int8 highNote; int8 lowVelocity; int8 highVelocity; int16 gain; Loop sustainLoop; Loop releaseLoop; void copyTo (StringPairArray& values) const { values.set ("MidiUnityNote", String (baseNote)); values.set ("Detune", String (detune)); values.set ("LowNote", String (lowNote)); values.set ("HighNote", String (highNote)); values.set ("LowVelocity", String (lowVelocity)); values.set ("HighVelocity", String (highVelocity)); values.set ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain))); values.set ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more values.set ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type))); values.set ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier))); values.set ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier))); values.set ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type))); values.set ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier))); values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier))); } static uint16 getValue16 (const StringPairArray& values, const char* name, const char* def) { return ByteOrder::swapIfLittleEndian ((uint16) values.getValue (name, def).getIntValue()); } static int8 getValue8 (const StringPairArray& values, const char* name, const char* def) { return (int8) values.getValue (name, def).getIntValue(); } static void create (MemoryBlock& block, const StringPairArray& values) { if (values.getAllKeys().contains ("MidiUnityNote", true)) { block.setSize ((sizeof (InstChunk) + 3) & ~(size_t) 3, true); InstChunk& inst = *static_cast (block.getData()); inst.baseNote = getValue8 (values, "MidiUnityNote", "60"); inst.detune = getValue8 (values, "Detune", "0"); inst.lowNote = getValue8 (values, "LowNote", "0"); inst.highNote = getValue8 (values, "HighNote", "127"); inst.lowVelocity = getValue8 (values, "LowVelocity", "1"); inst.highVelocity = getValue8 (values, "HighVelocity", "127"); inst.gain = (int16) getValue16 (values, "Gain", "0"); inst.sustainLoop.type = getValue16 (values, "Loop0Type", "0"); inst.sustainLoop.startIdentifier = getValue16 (values, "Loop0StartIdentifier", "0"); inst.sustainLoop.endIdentifier = getValue16 (values, "Loop0EndIdentifier", "0"); inst.releaseLoop.type = getValue16 (values, "Loop1Type", "0"); inst.releaseLoop.startIdentifier = getValue16 (values, "Loop1StartIdentifier", "0"); inst.releaseLoop.endIdentifier = getValue16 (values, "Loop1EndIdentifier", "0"); } } } JUCE_PACKED; //============================================================================== struct BASCChunk { enum Key { minor = 1, major = 2, neither = 3, both = 4 }; BASCChunk (InputStream& input) { zerostruct (*this); flags = (uint32) input.readIntBigEndian(); numBeats = (uint32) input.readIntBigEndian(); rootNote = (uint16) input.readShortBigEndian(); key = (uint16) input.readShortBigEndian(); timeSigNum = (uint16) input.readShortBigEndian(); timeSigDen = (uint16) input.readShortBigEndian(); oneShot = (uint16) input.readShortBigEndian(); input.read (unknown, sizeof (unknown)); } void addToMetadata (StringPairArray& metadata) const { const bool rootNoteSet = rootNote != 0; setBoolFlag (metadata, AiffAudioFormat::appleOneShot, oneShot == 2); setBoolFlag (metadata, AiffAudioFormat::appleRootSet, rootNoteSet); if (rootNoteSet) metadata.set (AiffAudioFormat::appleRootNote, String (rootNote)); metadata.set (AiffAudioFormat::appleBeats, String (numBeats)); metadata.set (AiffAudioFormat::appleDenominator, String (timeSigDen)); metadata.set (AiffAudioFormat::appleNumerator, String (timeSigNum)); const char* keyString = nullptr; switch (key) { case minor: keyString = "major"; break; case major: keyString = "major"; break; case neither: keyString = "neither"; break; case both: keyString = "both"; break; } if (keyString != nullptr) metadata.set (AiffAudioFormat::appleKey, keyString); } void setBoolFlag (StringPairArray& values, const char* name, bool shouldBeSet) const { values.set (name, shouldBeSet ? "1" : "0"); } uint32 flags; uint32 numBeats; uint16 rootNote; uint16 key; uint16 timeSigNum; uint16 timeSigDen; uint16 oneShot; uint8 unknown[66]; } JUCE_PACKED; #if JUCE_MSVC #pragma pack (pop) #endif //============================================================================== namespace CATEChunk { static bool isValidTag (const char* d) noexcept { return CharacterFunctions::isLetterOrDigit (d[0]) && CharacterFunctions::isUpperCase (d[0]) && CharacterFunctions::isLetterOrDigit (d[1]) && CharacterFunctions::isLowerCase (d[1]) && CharacterFunctions::isLetterOrDigit (d[2]) && CharacterFunctions::isLowerCase (d[2]); } static bool isAppleGenre (const String& tag) noexcept { static const char* appleGenres[] = { "Rock/Blues", "Electronic/Dance", "Jazz", "Urban", "World/Ethnic", "Cinematic/New Age", "Orchestral", "Country/Folk", "Experimental", "Other Genre" }; for (int i = 0; i < numElementsInArray (appleGenres); ++i) if (tag == appleGenres[i]) return true; return false; } static String read (InputStream& input, const uint32 length) { MemoryBlock mb; input.skipNextBytes (4); input.readIntoMemoryBlock (mb, (ssize_t) length - 4); StringArray tagsArray; const char* data = static_cast (mb.getData()); const char* dataEnd = data + mb.getSize(); while (data < dataEnd) { bool isGenre = false; if (isValidTag (data)) { const String tag = String (CharPointer_UTF8 (data), CharPointer_UTF8 (dataEnd)); isGenre = isAppleGenre (tag); tagsArray.add (tag); } data += isGenre ? 118 : 50; if (data[0] == 0) { if (data + 52 < dataEnd && isValidTag (data + 50)) data += 50; else if (data + 120 < dataEnd && isValidTag (data + 118)) data += 118; else if (data + 170 < dataEnd && isValidTag (data + 168)) data += 168; } } return tagsArray.joinIntoString (";"); } } //============================================================================== namespace MarkChunk { static bool metaDataContainsZeroIdentifiers (const StringPairArray& values) { // (zero cue identifiers are valid for WAV but not for AIFF) const String cueString ("Cue"); const String noteString ("CueNote"); const String identifierString ("Identifier"); const StringArray& keys = values.getAllKeys(); for (int i = 0; i < keys.size(); ++i) { const String key (keys[i]); if (key.startsWith (noteString)) continue; // zero identifier IS valid in a COMT chunk if (key.startsWith (cueString) && key.contains (identifierString)) { const int value = values.getValue (key, "-1").getIntValue(); if (value == 0) return true; } } return false; } static void create (MemoryBlock& block, const StringPairArray& values) { const int numCues = values.getValue ("NumCuePoints", "0").getIntValue(); if (numCues > 0) { MemoryOutputStream out (block, false); out.writeShortBigEndian ((short) numCues); const int numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue(); const int idOffset = metaDataContainsZeroIdentifiers (values) ? 1 : 0; // can't have zero IDs in AIFF #if JUCE_DEBUG Array identifiers; #endif for (int i = 0; i < numCues; ++i) { const String prefixCue ("Cue" + String (i)); const int identifier = idOffset + values.getValue (prefixCue + "Identifier", "1").getIntValue(); #if JUCE_DEBUG jassert (! identifiers.contains (identifier)); identifiers.add (identifier); #endif const int offset = values.getValue (prefixCue + "Offset", "0").getIntValue(); String label ("CueLabel" + String (i)); for (int labelIndex = 0; labelIndex < numCueLabels; ++labelIndex) { const String prefixLabel ("CueLabel" + String (labelIndex)); const int labelIdentifier = idOffset + values.getValue (prefixLabel + "Identifier", "1").getIntValue(); if (labelIdentifier == identifier) { label = values.getValue (prefixLabel + "Text", label); break; } } out.writeShortBigEndian ((short) identifier); out.writeIntBigEndian (offset); const size_t labelLength = jmin ((size_t) 254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring out.writeByte ((char) labelLength + 1); out.write (label.toUTF8(), labelLength); out.writeByte (0); if ((out.getDataSize() & 1) != 0) out.writeByte (0); } } } } //============================================================================== namespace COMTChunk { static void create (MemoryBlock& block, const StringPairArray& values) { const int numNotes = values.getValue ("NumCueNotes", "0").getIntValue(); if (numNotes > 0) { MemoryOutputStream out (block, false); out.writeShortBigEndian ((short) numNotes); for (int i = 0; i < numNotes; ++i) { const String prefix ("CueNote" + String (i)); out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue()); out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue()); const String comment (values.getValue (prefix + "Text", String())); const size_t commentLength = jmin (comment.getNumBytesAsUTF8(), (size_t) 65534); out.writeShortBigEndian ((short) commentLength + 1); out.write (comment.toUTF8(), commentLength); out.writeByte (0); if ((out.getDataSize() & 1) != 0) out.writeByte (0); } } } } } //============================================================================== class AiffAudioFormatReader : public AudioFormatReader { public: AiffAudioFormatReader (InputStream* in) : AudioFormatReader (in, aiffFormatName) { using namespace AiffFileHelpers; if (input->readInt() == chunkName ("FORM")) { const int len = input->readIntBigEndian(); const int64 end = input->getPosition() + len; const int nextType = input->readInt(); if (nextType == chunkName ("AIFF") || nextType == chunkName ("AIFC")) { bool hasGotVer = false; bool hasGotData = false; bool hasGotType = false; while (input->getPosition() < end) { const int type = input->readInt(); const uint32 length = (uint32) input->readIntBigEndian(); const int64 chunkEnd = input->getPosition() + length; if (type == chunkName ("FVER")) { hasGotVer = true; const int ver = input->readIntBigEndian(); if (ver != 0 && ver != (int) 0xa2805140) break; } else if (type == chunkName ("COMM")) { hasGotType = true; numChannels = (unsigned int) input->readShortBigEndian(); lengthInSamples = input->readIntBigEndian(); bitsPerSample = (unsigned int) input->readShortBigEndian(); bytesPerFrame = (int) ((numChannels * bitsPerSample) >> 3); unsigned char sampleRateBytes[10]; input->read (sampleRateBytes, 10); const int byte0 = sampleRateBytes[0]; if ((byte0 & 0x80) != 0 || byte0 <= 0x3F || byte0 > 0x40 || (byte0 == 0x40 && sampleRateBytes[1] > 0x1C)) break; unsigned int sampRate = ByteOrder::bigEndianInt (sampleRateBytes + 2); sampRate >>= (16414 - ByteOrder::bigEndianShort (sampleRateBytes)); sampleRate = (int) sampRate; if (length <= 18) { // some types don't have a chunk large enough to include a compression // type, so assume it's just big-endian pcm littleEndian = false; } else { const int compType = input->readInt(); if (compType == chunkName ("NONE") || compType == chunkName ("twos")) { littleEndian = false; } else if (compType == chunkName ("sowt")) { littleEndian = true; } else if (compType == chunkName ("fl32") || compType == chunkName ("FL32")) { littleEndian = false; usesFloatingPointData = true; } else { sampleRate = 0; break; } } } else if (type == chunkName ("SSND")) { hasGotData = true; const int offset = input->readIntBigEndian(); dataChunkStart = input->getPosition() + 4 + offset; lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, ((int64) length) / (int64) bytesPerFrame) : 0; } else if (type == chunkName ("MARK")) { const uint16 numCues = (uint16) input->readShortBigEndian(); // these two are always the same for AIFF-read files metadataValues.set ("NumCuePoints", String (numCues)); metadataValues.set ("NumCueLabels", String (numCues)); for (uint16 i = 0; i < numCues; ++i) { uint16 identifier = (uint16) input->readShortBigEndian(); uint32 offset = (uint32) input->readIntBigEndian(); uint8 stringLength = (uint8) input->readByte(); MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, stringLength); // if the stringLength is even then read one more byte as the // string needs to be an even number of bytes INCLUDING the // leading length character in the pascal string if ((stringLength & 1) == 0) input->readByte(); const String prefixCue ("Cue" + String (i)); metadataValues.set (prefixCue + "Identifier", String (identifier)); metadataValues.set (prefixCue + "Offset", String (offset)); const String prefixLabel ("CueLabel" + String (i)); metadataValues.set (prefixLabel + "Identifier", String (identifier)); metadataValues.set (prefixLabel + "Text", textBlock.toString()); } } else if (type == chunkName ("COMT")) { const uint16 numNotes = (uint16) input->readShortBigEndian(); metadataValues.set ("NumCueNotes", String (numNotes)); for (uint16 i = 0; i < numNotes; ++i) { uint32 timestamp = (uint32) input->readIntBigEndian(); uint16 identifier = (uint16) input->readShortBigEndian(); // may be zero in this case uint16 stringLength = (uint16) input->readShortBigEndian(); MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1)); const String prefix ("CueNote" + String (i)); metadataValues.set (prefix + "TimeStamp", String (timestamp)); metadataValues.set (prefix + "Identifier", String (identifier)); metadataValues.set (prefix + "Text", textBlock.toString()); } } else if (type == chunkName ("INST")) { HeapBlock inst; inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); input->read (inst, (int) length); inst->copyTo (metadataValues); } else if (type == chunkName ("basc")) { AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValues); } else if (type == chunkName ("cate")) { metadataValues.set (AiffAudioFormat::appleTag, AiffFileHelpers::CATEChunk::read (*input, length)); } else if ((hasGotVer && hasGotData && hasGotType) || chunkEnd < input->getPosition() || input->isExhausted()) { break; } input->setPosition (chunkEnd + (chunkEnd & 1)); // (chunks should be aligned to an even byte address) } } } if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "AIFF"); } //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); if (numSamples <= 0) return true; input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); while (numSamples > 0) { const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) char tempBuffer [tempBufSize]; const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples); const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); if (bytesRead < numThisTime * bytesPerFrame) { jassert (bytesRead >= 0); zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead)); } if (littleEndian) copySampleData (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); else copySampleData (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); startOffsetInDestBuffer += numThisTime; numSamples -= numThisTime; } return true; } template static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData, int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels, const void* sourceData, int numChannels, int numSamples) noexcept { switch (bitsPerSample) { case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; case 32: if (usesFloatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; default: jassertfalse; break; } } int bytesPerFrame; int64 dataChunkStart; bool littleEndian; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader) }; //============================================================================== class AiffAudioFormatWriter : public AudioFormatWriter { public: AiffAudioFormatWriter (OutputStream* out, double rate, unsigned int numChans, unsigned int bits, const StringPairArray& metadataValues) : AudioFormatWriter (out, aiffFormatName, rate, numChans, bits), lengthInSamples (0), bytesWritten (0), writeFailed (false) { using namespace AiffFileHelpers; if (metadataValues.size() > 0) { // The meta data should have been santised for the AIFF format. // If it was originally sourced from a WAV file the MetaDataSource // key should be removed (or set to "AIFF") once this has been done jassert (metadataValues.getValue ("MetaDataSource", "None") != "WAV"); MarkChunk::create (markChunk, metadataValues); COMTChunk::create (comtChunk, metadataValues); InstChunk::create (instChunk, metadataValues); } headerPosition = out->getPosition(); writeHeader(); } ~AiffAudioFormatWriter() { if ((bytesWritten & 1) != 0) output->writeByte (0); writeHeader(); } //============================================================================== bool write (const int** data, int numSamples) override { jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel! if (writeFailed) return false; const size_t bytes = (size_t) numSamples * numChannels * bitsPerSample / 8; tempBlock.ensureSize ((size_t) bytes, false); switch (bitsPerSample) { case 8: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; case 16: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; case 24: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; case 32: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; default: jassertfalse; break; } if (bytesWritten + bytes >= (size_t) 0xfff00000 || ! output->write (tempBlock.getData(), bytes)) { // failed to write to disk, so let's try writing the header. // If it's just run out of disk space, then if it does manage // to write the header, we'll still have a useable file.. writeHeader(); writeFailed = true; return false; } else { bytesWritten += bytes; lengthInSamples += (uint64) numSamples; return true; } } private: MemoryBlock tempBlock, markChunk, comtChunk, instChunk; uint64 lengthInSamples, bytesWritten; int64 headerPosition; bool writeFailed; void writeHeader() { using namespace AiffFileHelpers; const bool couldSeekOk = output->setPosition (headerPosition); (void) couldSeekOk; // if this fails, you've given it an output stream that can't seek! It needs // to be able to seek back to write the header jassert (couldSeekOk); const int headerLen = (int) (54 + (markChunk.getSize() > 0 ? markChunk.getSize() + 8 : 0) + (comtChunk.getSize() > 0 ? comtChunk.getSize() + 8 : 0) + (instChunk.getSize() > 0 ? instChunk.getSize() + 8 : 0)); int audioBytes = (int) (lengthInSamples * ((bitsPerSample * numChannels) / 8)); audioBytes += (audioBytes & 1); output->writeInt (chunkName ("FORM")); output->writeIntBigEndian (headerLen + audioBytes - 8); output->writeInt (chunkName ("AIFF")); output->writeInt (chunkName ("COMM")); output->writeIntBigEndian (18); output->writeShortBigEndian ((short) numChannels); output->writeIntBigEndian ((int) lengthInSamples); output->writeShortBigEndian ((short) bitsPerSample); uint8 sampleRateBytes[10] = { 0 }; if (sampleRate <= 1) { sampleRateBytes[0] = 0x3f; sampleRateBytes[1] = 0xff; sampleRateBytes[2] = 0x80; } else { int mask = 0x40000000; sampleRateBytes[0] = 0x40; if (sampleRate >= mask) { jassertfalse; sampleRateBytes[1] = 0x1d; } else { int n = (int) sampleRate; int i; for (i = 0; i <= 32 ; ++i) { if ((n & mask) != 0) break; mask >>= 1; } n = n << (i + 1); sampleRateBytes[1] = (uint8) (29 - i); sampleRateBytes[2] = (uint8) ((n >> 24) & 0xff); sampleRateBytes[3] = (uint8) ((n >> 16) & 0xff); sampleRateBytes[4] = (uint8) ((n >> 8) & 0xff); sampleRateBytes[5] = (uint8) (n & 0xff); } } output->write (sampleRateBytes, 10); if (markChunk.getSize() > 0) { output->writeInt (chunkName ("MARK")); output->writeIntBigEndian ((int) markChunk.getSize()); *output << markChunk; } if (comtChunk.getSize() > 0) { output->writeInt (chunkName ("COMT")); output->writeIntBigEndian ((int) comtChunk.getSize()); *output << comtChunk; } if (instChunk.getSize() > 0) { output->writeInt (chunkName ("INST")); output->writeIntBigEndian ((int) instChunk.getSize()); *output << instChunk; } output->writeInt (chunkName ("SSND")); output->writeIntBigEndian (audioBytes + 8); output->writeInt (0); output->writeInt (0); jassert (output->getPosition() == headerLen); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter) }; //============================================================================== class MemoryMappedAiffReader : public MemoryMappedAudioFormatReader { public: MemoryMappedAiffReader (const File& f, const AiffAudioFormatReader& reader) : MemoryMappedAudioFormatReader (f, reader, reader.dataChunkStart, reader.bytesPerFrame * reader.lengthInSamples, reader.bytesPerFrame), littleEndian (reader.littleEndian) { } bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. return false; } if (littleEndian) AiffAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples); else AiffAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples); return true; } void getSample (int64 sample, float* result) const noexcept override { const int num = (int) numChannels; if (map == nullptr || ! mappedSection.contains (sample)) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. zeromem (result, sizeof (float) * (size_t) num); return; } float** dest = &result; const void* source = sampleToPointer (sample); if (littleEndian) { switch (bitsPerSample) { case 8: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 16: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 24: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 32: if (usesFloatingPointData) ReadHelper::read (dest, 0, 1, source, 1, num); else ReadHelper::read (dest, 0, 1, source, 1, num); break; default: jassertfalse; break; } } else { switch (bitsPerSample) { case 8: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 16: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 24: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 32: if (usesFloatingPointData) ReadHelper::read (dest, 0, 1, source, 1, num); else ReadHelper::read (dest, 0, 1, source, 1, num); break; default: jassertfalse; break; } } } void readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) override { if (numSamples <= 0) { for (int i = 0; i < numChannelsToRead; ++i) results[i] = Range(); return; } if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. for (int i = 0; i < numChannelsToRead; ++i) results[i] = Range(); return; } switch (bitsPerSample) { case 8: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; case 16: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; case 24: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); else scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; default: jassertfalse; break; } } private: const bool littleEndian; template void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) const noexcept { for (int i = 0; i < numChannelsToRead; ++i) results[i] = scanMinAndMaxForChannel (i, startSampleInFile, numSamples); } template Range scanMinAndMaxForChannel (int channel, int64 startSampleInFile, int64 numSamples) const noexcept { return littleEndian ? scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples) : scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAiffReader) }; //============================================================================== AiffAudioFormat::AiffAudioFormat() : AudioFormat (aiffFormatName, ".aiff .aif") { } AiffAudioFormat::~AiffAudioFormat() { } Array AiffAudioFormat::getPossibleSampleRates() { const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; return Array (rates); } Array AiffAudioFormat::getPossibleBitDepths() { const int depths[] = { 8, 16, 24, 0 }; return Array (depths); } bool AiffAudioFormat::canDoStereo() { return true; } bool AiffAudioFormat::canDoMono() { return true; } #if JUCE_MAC bool AiffAudioFormat::canHandleFile (const File& f) { if (AudioFormat::canHandleFile (f)) return true; const OSType type = f.getMacOSType(); // (NB: written as hex to avoid four-char-constant warnings) return type == 0x41494646 /* AIFF */ || type == 0x41494643 /* AIFC */ || type == 0x61696666 /* aiff */ || type == 0x61696663 /* aifc */; } #endif AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) { ScopedPointer w (new AiffAudioFormatReader (sourceStream)); if (w->sampleRate > 0 && w->numChannels > 0) return w.release(); if (! deleteStreamIfOpeningFails) w->input = nullptr; return nullptr; } MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (const File& file) { if (FileInputStream* fin = file.createInputStream()) { AiffAudioFormatReader reader (fin); if (reader.lengthInSamples > 0) return new MemoryMappedAiffReader (file, reader); } return nullptr; } AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, double sampleRate, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int /*qualityOptionIndex*/) { if (getPossibleBitDepths().contains (bitsPerSample)) return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, (unsigned int) bitsPerSample, metadataValues); return nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h000066400000000000000000000070771320201440200331000ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ //============================================================================== /** Reads and Writes AIFF format audio files. @see AudioFormat */ class JUCE_API AiffAudioFormat : public AudioFormat { public: //============================================================================== /** Creates an format object. */ AiffAudioFormat(); /** Destructor. */ ~AiffAudioFormat(); //============================================================================== /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleOneShot; /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleRootSet; /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleRootNote; /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleBeats; /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleDenominator; /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleNumerator; /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleTag; /** Metadata property name used when reading a aiff file with a basc chunk. */ static const char* const appleKey; //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; #if JUCE_MAC bool canHandleFile (const File& fileToTest) override; #endif //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) override; MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AiffAudioFormat) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp000066400000000000000000000477411320201440200334600ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_MAC || JUCE_IOS //============================================================================== namespace { const char* const coreAudioFormatName = "CoreAudio supported file"; StringArray findFileExtensionsForCoreAudioCodecs() { StringArray extensionsArray; CFArrayRef extensions = nullptr; UInt32 sizeOfArray = sizeof (extensions); if (AudioFileGetGlobalInfo (kAudioFileGlobalInfo_AllExtensions, 0, 0, &sizeOfArray, &extensions) == noErr) { const CFIndex numValues = CFArrayGetCount (extensions); for (CFIndex i = 0; i < numValues; ++i) extensionsArray.add ("." + String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (extensions, i))); CFRelease (extensions); } return extensionsArray; } } //============================================================================== const char* const CoreAudioFormat::midiDataBase64 = "midiDataBase64"; const char* const CoreAudioFormat::tempo = "tempo"; const char* const CoreAudioFormat::timeSig = "time signature"; const char* const CoreAudioFormat::keySig = "key signature"; //============================================================================== struct CoreAudioFormatMetatdata { static uint32 chunkName (const char* const name) noexcept { return ByteOrder::bigEndianInt (name); } //============================================================================== struct FileHeader { FileHeader (InputStream& input) { fileType = (uint32) input.readIntBigEndian(); fileVersion = (uint16) input.readShortBigEndian(); fileFlags = (uint16) input.readShortBigEndian(); } uint32 fileType; uint16 fileVersion; uint16 fileFlags; }; //============================================================================== struct ChunkHeader { ChunkHeader (InputStream& input) { chunkType = (uint32) input.readIntBigEndian(); chunkSize = (int64) input.readInt64BigEndian(); } uint32 chunkType; int64 chunkSize; }; //============================================================================== struct AudioDescriptionChunk { AudioDescriptionChunk (InputStream& input) { sampleRate = input.readDoubleBigEndian(); formatID = (uint32) input.readIntBigEndian(); formatFlags = (uint32) input.readIntBigEndian(); bytesPerPacket = (uint32) input.readIntBigEndian(); framesPerPacket = (uint32) input.readIntBigEndian(); channelsPerFrame = (uint32) input.readIntBigEndian(); bitsPerChannel = (uint32) input.readIntBigEndian(); } double sampleRate; uint32 formatID; uint32 formatFlags; uint32 bytesPerPacket; uint32 framesPerPacket; uint32 channelsPerFrame; uint32 bitsPerChannel; }; //============================================================================== struct UserDefinedChunk { UserDefinedChunk (InputStream& input, int64 size) { // a user defined chunk contains 16 bytes of a UUID first uuid[1] = input.readInt64BigEndian(); uuid[0] = input.readInt64BigEndian(); input.skipNextBytes (size - 16); } int64 uuid[2]; }; //============================================================================== static StringPairArray parseMidiChunk (InputStream& input, int64 size) { const int64 originalPosition = input.getPosition(); MemoryBlock midiBlock; input.readIntoMemoryBlock (midiBlock, (ssize_t) size); MemoryInputStream midiInputStream (midiBlock, false); StringPairArray midiMetadata; MidiFile midiFile; if (midiFile.readFrom (midiInputStream)) { midiMetadata.set (CoreAudioFormat::midiDataBase64, midiBlock.toBase64Encoding()); findTempoEvents (midiFile, midiMetadata); findTimeSigEvents (midiFile, midiMetadata); findKeySigEvents (midiFile, midiMetadata); } input.setPosition (originalPosition + size); return midiMetadata; } static void findTempoEvents (MidiFile& midiFile, StringPairArray& midiMetadata) { MidiMessageSequence tempoEvents; midiFile.findAllTempoEvents (tempoEvents); const int numTempoEvents = tempoEvents.getNumEvents(); MemoryOutputStream tempoSequence; for (int i = 0; i < numTempoEvents; ++i) { const double tempo = getTempoFromTempoMetaEvent (tempoEvents.getEventPointer (i)); if (tempo > 0.0) { if (i == 0) midiMetadata.set (CoreAudioFormat::tempo, String (tempo)); if (numTempoEvents > 1) tempoSequence << String (tempo) << ',' << tempoEvents.getEventTime (i) << ';'; } } if (tempoSequence.getDataSize() > 0) midiMetadata.set ("tempo sequence", tempoSequence.toUTF8()); } static double getTempoFromTempoMetaEvent (MidiMessageSequence::MidiEventHolder* holder) { if (holder != nullptr) { const MidiMessage& midiMessage = holder->message; if (midiMessage.isTempoMetaEvent()) { const double tempoSecondsPerQuarterNote = midiMessage.getTempoSecondsPerQuarterNote(); if (tempoSecondsPerQuarterNote > 0.0) return 60.0 / tempoSecondsPerQuarterNote; } } return 0.0; } static void findTimeSigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) { MidiMessageSequence timeSigEvents; midiFile.findAllTimeSigEvents (timeSigEvents); const int numTimeSigEvents = timeSigEvents.getNumEvents(); MemoryOutputStream timeSigSequence; for (int i = 0; i < numTimeSigEvents; ++i) { int numerator, denominator; timeSigEvents.getEventPointer(i)->message.getTimeSignatureInfo (numerator, denominator); String timeSigString; timeSigString << numerator << '/' << denominator; if (i == 0) midiMetadata.set (CoreAudioFormat::timeSig, timeSigString); if (numTimeSigEvents > 1) timeSigSequence << timeSigString << ',' << timeSigEvents.getEventTime (i) << ';'; } if (timeSigSequence.getDataSize() > 0) midiMetadata.set ("time signature sequence", timeSigSequence.toUTF8()); } static void findKeySigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) { MidiMessageSequence keySigEvents; midiFile.findAllKeySigEvents (keySigEvents); const int numKeySigEvents = keySigEvents.getNumEvents(); MemoryOutputStream keySigSequence; for (int i = 0; i < numKeySigEvents; ++i) { const MidiMessage& message (keySigEvents.getEventPointer (i)->message); const int key = jlimit (0, 14, message.getKeySignatureNumberOfSharpsOrFlats() + 7); const bool isMajor = message.isKeySignatureMajorKey(); static const char* majorKeys[] = { "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#" }; static const char* minorKeys[] = { "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#" }; String keySigString (isMajor ? majorKeys[key] : minorKeys[key]); if (! isMajor) keySigString << 'm'; if (i == 0) midiMetadata.set (CoreAudioFormat::keySig, keySigString); if (numKeySigEvents > 1) keySigSequence << keySigString << ',' << keySigEvents.getEventTime (i) << ';'; } if (keySigSequence.getDataSize() > 0) midiMetadata.set ("key signature sequence", keySigSequence.toUTF8()); } //============================================================================== static StringPairArray parseInformationChunk (InputStream& input) { StringPairArray infoStrings; const uint32 numEntries = (uint32) input.readIntBigEndian(); for (uint32 i = 0; i < numEntries; ++i) infoStrings.set (input.readString(), input.readString()); return infoStrings; } //============================================================================== static bool read (InputStream& input, StringPairArray& metadataValues) { const int64 originalPos = input.getPosition(); const FileHeader cafFileHeader (input); const bool isCafFile = cafFileHeader.fileType == chunkName ("caff"); if (isCafFile) { while (! input.isExhausted()) { const ChunkHeader chunkHeader (input); if (chunkHeader.chunkType == chunkName ("desc")) { AudioDescriptionChunk audioDescriptionChunk (input); } else if (chunkHeader.chunkType == chunkName ("uuid")) { UserDefinedChunk userDefinedChunk (input, chunkHeader.chunkSize); } else if (chunkHeader.chunkType == chunkName ("data")) { // -1 signifies an unknown data size so the data has to be at the // end of the file so we must have finished the header if (chunkHeader.chunkSize == -1) break; input.skipNextBytes (chunkHeader.chunkSize); } else if (chunkHeader.chunkType == chunkName ("midi")) { metadataValues.addArray (parseMidiChunk (input, chunkHeader.chunkSize)); } else if (chunkHeader.chunkType == chunkName ("info")) { metadataValues.addArray (parseInformationChunk (input)); } else { // we aren't decoding this chunk yet so just skip over it input.skipNextBytes (chunkHeader.chunkSize); } } } input.setPosition (originalPos); return isCafFile; } }; //============================================================================== class CoreAudioReader : public AudioFormatReader { public: CoreAudioReader (InputStream* const inp) : AudioFormatReader (inp, coreAudioFormatName), ok (false), lastReadPosition (0) { usesFloatingPointData = true; bitsPerSample = 32; if (input != nullptr) CoreAudioFormatMetatdata::read (*input, metadataValues); OSStatus status = AudioFileOpenWithCallbacks (this, &readCallback, nullptr, // write needs to be null to avoid permisisions errors &getSizeCallback, nullptr, // setSize needs to be null to avoid permisisions errors 0, // AudioFileTypeID inFileTypeHint &audioFileID); if (status == noErr) { status = ExtAudioFileWrapAudioFileID (audioFileID, false, &audioFileRef); if (status == noErr) { AudioStreamBasicDescription sourceAudioFormat; UInt32 audioStreamBasicDescriptionSize = sizeof (AudioStreamBasicDescription); ExtAudioFileGetProperty (audioFileRef, kExtAudioFileProperty_FileDataFormat, &audioStreamBasicDescriptionSize, &sourceAudioFormat); numChannels = sourceAudioFormat.mChannelsPerFrame; sampleRate = sourceAudioFormat.mSampleRate; UInt32 sizeOfLengthProperty = sizeof (int64); ExtAudioFileGetProperty (audioFileRef, kExtAudioFileProperty_FileLengthFrames, &sizeOfLengthProperty, &lengthInSamples); destinationAudioFormat.mSampleRate = sampleRate; destinationAudioFormat.mFormatID = kAudioFormatLinearPCM; destinationAudioFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian; destinationAudioFormat.mBitsPerChannel = sizeof (float) * 8; destinationAudioFormat.mChannelsPerFrame = numChannels; destinationAudioFormat.mBytesPerFrame = sizeof (float); destinationAudioFormat.mFramesPerPacket = 1; destinationAudioFormat.mBytesPerPacket = destinationAudioFormat.mFramesPerPacket * destinationAudioFormat.mBytesPerFrame; status = ExtAudioFileSetProperty (audioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof (AudioStreamBasicDescription), &destinationAudioFormat); if (status == noErr) { bufferList.malloc (1, sizeof (AudioBufferList) + numChannels * sizeof (AudioBuffer)); bufferList->mNumberBuffers = numChannels; ok = true; } } } } ~CoreAudioReader() { ExtAudioFileDispose (audioFileRef); AudioFileClose (audioFileID); } //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); if (numSamples <= 0) return true; if (lastReadPosition != startSampleInFile) { OSStatus status = ExtAudioFileSeek (audioFileRef, startSampleInFile); if (status != noErr) return false; lastReadPosition = startSampleInFile; } while (numSamples > 0) { const int numThisTime = jmin (8192, numSamples); const size_t numBytes = sizeof (float) * (size_t) numThisTime; audioDataBlock.ensureSize (numBytes * numChannels, false); float* data = static_cast (audioDataBlock.getData()); for (int j = (int) numChannels; --j >= 0;) { bufferList->mBuffers[j].mNumberChannels = 1; bufferList->mBuffers[j].mDataByteSize = (UInt32) numBytes; bufferList->mBuffers[j].mData = data; data += numThisTime; } UInt32 numFramesToRead = (UInt32) numThisTime; OSStatus status = ExtAudioFileRead (audioFileRef, &numFramesToRead, bufferList); if (status != noErr) return false; for (int i = numDestChannels; --i >= 0;) { if (destSamples[i] != nullptr) { if (i < (int) numChannels) memcpy (destSamples[i] + startOffsetInDestBuffer, bufferList->mBuffers[i].mData, numBytes); else zeromem (destSamples[i] + startOffsetInDestBuffer, numBytes); } } startOffsetInDestBuffer += numThisTime; numSamples -= numThisTime; lastReadPosition += numThisTime; } return true; } bool ok; private: AudioFileID audioFileID; ExtAudioFileRef audioFileRef; AudioStreamBasicDescription destinationAudioFormat; MemoryBlock audioDataBlock; HeapBlock bufferList; int64 lastReadPosition; static SInt64 getSizeCallback (void* inClientData) { return static_cast (inClientData)->input->getTotalLength(); } static OSStatus readCallback (void* inClientData, SInt64 inPosition, UInt32 requestCount, void* buffer, UInt32* actualCount) { CoreAudioReader* const reader = static_cast (inClientData); reader->input->setPosition (inPosition); *actualCount = (UInt32) reader->input->read (buffer, (int) requestCount); return noErr; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioReader) }; //============================================================================== CoreAudioFormat::CoreAudioFormat() : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs()) { } CoreAudioFormat::~CoreAudioFormat() {} Array CoreAudioFormat::getPossibleSampleRates() { return Array(); } Array CoreAudioFormat::getPossibleBitDepths() { return Array(); } bool CoreAudioFormat::canDoStereo() { return true; } bool CoreAudioFormat::canDoMono() { return true; } //============================================================================== AudioFormatReader* CoreAudioFormat::createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) { ScopedPointer r (new CoreAudioReader (sourceStream)); if (r->ok) return r.release(); if (! deleteStreamIfOpeningFails) r->input = nullptr; return nullptr; } AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*, double /*sampleRateToUse*/, unsigned int /*numberOfChannels*/, int /*bitsPerSample*/, const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/) { jassertfalse; // not yet implemented! return nullptr; } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h000066400000000000000000000061021320201440200331070ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_MAC || JUCE_IOS || DOXYGEN //============================================================================== /** OSX and iOS only - This uses the AudioToolbox framework to read any audio format that the system has a codec for. This should be able to understand formats such as mp3, m4a, etc. @see AudioFormat */ class JUCE_API CoreAudioFormat : public AudioFormat { public: //============================================================================== /** Creates a format object. */ CoreAudioFormat(); /** Destructor. */ ~CoreAudioFormat(); //============================================================================== /** Metadata property name used when reading a caf file with a MIDI chunk. */ static const char* const midiDataBase64; /** Metadata property name used when reading a caf file with tempo information. */ static const char* const tempo; /** Metadata property name used when reading a caf file time signature information. */ static const char* const timeSig; /** Metadata property name used when reading a caf file time signature information. */ static const char* const keySig; //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioFormat) }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp000066400000000000000000000514731320201440200334320ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_FLAC namespace FlacNamespace { #if JUCE_INCLUDE_FLAC_CODE || ! defined (JUCE_INCLUDE_FLAC_CODE) #undef VERSION #define VERSION "1.2.1" #define FLAC__NO_DLL 1 #if JUCE_MSVC #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111) #endif #if JUCE_MAC #define FLAC__SYS_DARWIN 1 #endif #ifndef SIZE_MAX #define SIZE_MAX 0xffffffff #endif #if JUCE_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #if JUCE_INTEL #if JUCE_32BIT #define FLAC__CPU_IA32 1 #endif #if JUCE_64BIT #define FLAC__CPU_X86_64 1 #endif #define FLAC__HAS_X86INTRIN 1 #endif #undef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #define flac_max jmax #define flac_min jmin #include "flac/all.h" #include "flac/libFLAC/bitmath.c" #include "flac/libFLAC/bitreader.c" #include "flac/libFLAC/bitwriter.c" #include "flac/libFLAC/cpu.c" #include "flac/libFLAC/crc.c" #include "flac/libFLAC/fixed.c" #include "flac/libFLAC/float.c" #include "flac/libFLAC/format.c" #include "flac/libFLAC/lpc_flac.c" #include "flac/libFLAC/md5.c" #include "flac/libFLAC/memory.c" #include "flac/libFLAC/stream_decoder.c" #include "flac/libFLAC/stream_encoder.c" #include "flac/libFLAC/stream_encoder_framing.c" #include "flac/libFLAC/window_flac.c" #undef VERSION #else #include #endif #if JUCE_CLANG #pragma clang diagnostic pop #endif } #undef max #undef min //============================================================================== static const char* const flacFormatName = "FLAC file"; //============================================================================== class FlacReader : public AudioFormatReader { public: FlacReader (InputStream* const in) : AudioFormatReader (in, flacFormatName), reservoirStart (0), samplesInReservoir (0), scanningForLength (false) { using namespace FlacNamespace; lengthInSamples = 0; decoder = FLAC__stream_decoder_new(); ok = FLAC__stream_decoder_init_stream (decoder, readCallback_, seekCallback_, tellCallback_, lengthCallback_, eofCallback_, writeCallback_, metadataCallback_, errorCallback_, this) == FLAC__STREAM_DECODER_INIT_STATUS_OK; if (ok) { FLAC__stream_decoder_process_until_end_of_metadata (decoder); if (lengthInSamples == 0 && sampleRate > 0) { // the length hasn't been stored in the metadata, so we'll need to // work it out the length the hard way, by scanning the whole file.. scanningForLength = true; FLAC__stream_decoder_process_until_end_of_stream (decoder); scanningForLength = false; const int64 tempLength = lengthInSamples; FLAC__stream_decoder_reset (decoder); FLAC__stream_decoder_process_until_end_of_metadata (decoder); lengthInSamples = tempLength; } } } ~FlacReader() { FlacNamespace::FLAC__stream_decoder_delete (decoder); } void useMetadata (const FlacNamespace::FLAC__StreamMetadata_StreamInfo& info) { sampleRate = info.sample_rate; bitsPerSample = info.bits_per_sample; lengthInSamples = (unsigned int) info.total_samples; numChannels = info.channels; reservoir.setSize ((int) numChannels, 2 * (int) info.max_blocksize, false, false, true); } // returns the number of samples read bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { using namespace FlacNamespace; if (! ok) return false; while (numSamples > 0) { if (startSampleInFile >= reservoirStart && startSampleInFile < reservoirStart + samplesInReservoir) { const int num = (int) jmin ((int64) numSamples, reservoirStart + samplesInReservoir - startSampleInFile); jassert (num > 0); for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), sizeof (int) * (size_t) num); startOffsetInDestBuffer += num; startSampleInFile += num; numSamples -= num; } else { if (startSampleInFile >= (int) lengthInSamples) { samplesInReservoir = 0; } else if (startSampleInFile < reservoirStart || startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511)) { // had some problems with flac crashing if the read pos is aligned more // accurately than this. Probably fixed in newer versions of the library, though. reservoirStart = (int) (startSampleInFile & ~511); samplesInReservoir = 0; FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64) reservoirStart); } else { reservoirStart += samplesInReservoir; samplesInReservoir = 0; FLAC__stream_decoder_process_single (decoder); } if (samplesInReservoir == 0) break; } } if (numSamples > 0) { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); } return true; } void useSamples (const FlacNamespace::FLAC__int32* const buffer[], int numSamples) { if (scanningForLength) { lengthInSamples += numSamples; } else { if (numSamples > reservoir.getNumSamples()) reservoir.setSize ((int) numChannels, numSamples, false, false, true); const unsigned int bitsToShift = 32 - bitsPerSample; for (int i = 0; i < (int) numChannels; ++i) { const FlacNamespace::FLAC__int32* src = buffer[i]; int n = i; while (src == 0 && n > 0) src = buffer [--n]; if (src != nullptr) { int* const dest = reinterpret_cast (reservoir.getWritePointer(i)); for (int j = 0; j < numSamples; ++j) dest[j] = src[j] << bitsToShift; } } samplesInReservoir = numSamples; } } //============================================================================== static FlacNamespace::FLAC__StreamDecoderReadStatus readCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__byte buffer[], size_t* bytes, void* client_data) { using namespace FlacNamespace; *bytes = (size_t) static_cast (client_data)->input->read (buffer, (int) *bytes); return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } static FlacNamespace::FLAC__StreamDecoderSeekStatus seekCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64 absolute_byte_offset, void* client_data) { using namespace FlacNamespace; static_cast (client_data)->input->setPosition ((int) absolute_byte_offset); return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } static FlacNamespace::FLAC__StreamDecoderTellStatus tellCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data) { using namespace FlacNamespace; *absolute_byte_offset = (uint64) static_cast (client_data)->input->getPosition(); return FLAC__STREAM_DECODER_TELL_STATUS_OK; } static FlacNamespace::FLAC__StreamDecoderLengthStatus lengthCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* stream_length, void* client_data) { using namespace FlacNamespace; *stream_length = (uint64) static_cast (client_data)->input->getTotalLength(); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } static FlacNamespace::FLAC__bool eofCallback_ (const FlacNamespace::FLAC__StreamDecoder*, void* client_data) { return static_cast (client_data)->input->isExhausted(); } static FlacNamespace::FLAC__StreamDecoderWriteStatus writeCallback_ (const FlacNamespace::FLAC__StreamDecoder*, const FlacNamespace::FLAC__Frame* frame, const FlacNamespace::FLAC__int32* const buffer[], void* client_data) { using namespace FlacNamespace; static_cast (client_data)->useSamples (buffer, (int) frame->header.blocksize); return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } static void metadataCallback_ (const FlacNamespace::FLAC__StreamDecoder*, const FlacNamespace::FLAC__StreamMetadata* metadata, void* client_data) { static_cast (client_data)->useMetadata (metadata->data.stream_info); } static void errorCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__StreamDecoderErrorStatus, void*) { } private: FlacNamespace::FLAC__StreamDecoder* decoder; AudioSampleBuffer reservoir; int reservoirStart, samplesInReservoir; bool ok, scanningForLength; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader) }; //============================================================================== class FlacWriter : public AudioFormatWriter { public: FlacWriter (OutputStream* const out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex) : AudioFormatWriter (out, flacFormatName, rate, numChans, bits) { using namespace FlacNamespace; encoder = FLAC__stream_encoder_new(); if (qualityOptionIndex > 0) FLAC__stream_encoder_set_compression_level (encoder, (uint32) jmin (8, qualityOptionIndex)); FLAC__stream_encoder_set_do_mid_side_stereo (encoder, numChannels == 2); FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, numChannels == 2); FLAC__stream_encoder_set_channels (encoder, numChannels); FLAC__stream_encoder_set_bits_per_sample (encoder, jmin ((unsigned int) 24, bitsPerSample)); FLAC__stream_encoder_set_sample_rate (encoder, (unsigned int) sampleRate); FLAC__stream_encoder_set_blocksize (encoder, 0); FLAC__stream_encoder_set_do_escape_coding (encoder, true); ok = FLAC__stream_encoder_init_stream (encoder, encodeWriteCallback, encodeSeekCallback, encodeTellCallback, encodeMetadataCallback, this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK; } ~FlacWriter() { if (ok) { FlacNamespace::FLAC__stream_encoder_finish (encoder); output->flush(); } else { output = nullptr; // to stop the base class deleting this, as it needs to be returned // to the caller of createWriter() } FlacNamespace::FLAC__stream_encoder_delete (encoder); } //============================================================================== bool write (const int** samplesToWrite, int numSamples) override { using namespace FlacNamespace; if (! ok) return false; HeapBlock channels; HeapBlock temp; const int bitsToShift = 32 - (int) bitsPerSample; if (bitsToShift > 0) { temp.malloc (numChannels * (size_t) numSamples); channels.calloc (numChannels + 1); for (unsigned int i = 0; i < numChannels; ++i) { if (samplesToWrite[i] == nullptr) break; int* const destData = temp.getData() + i * (size_t) numSamples; channels[i] = destData; for (int j = 0; j < numSamples; ++j) destData[j] = (samplesToWrite[i][j] >> bitsToShift); } samplesToWrite = const_cast (channels.getData()); } return FLAC__stream_encoder_process (encoder, (const FLAC__int32**) samplesToWrite, (unsigned) numSamples) != 0; } bool writeData (const void* const data, const int size) const { return output->write (data, (size_t) size); } static void packUint32 (FlacNamespace::FLAC__uint32 val, FlacNamespace::FLAC__byte* b, const int bytes) { b += bytes; for (int i = 0; i < bytes; ++i) { *(--b) = (FlacNamespace::FLAC__byte) (val & 0xff); val >>= 8; } } void writeMetaData (const FlacNamespace::FLAC__StreamMetadata* metadata) { using namespace FlacNamespace; const FLAC__StreamMetadata_StreamInfo& info = metadata->data.stream_info; unsigned char buffer [FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; const unsigned int channelsMinus1 = info.channels - 1; const unsigned int bitsMinus1 = info.bits_per_sample - 1; packUint32 (info.min_blocksize, buffer, 2); packUint32 (info.max_blocksize, buffer + 2, 2); packUint32 (info.min_framesize, buffer + 4, 3); packUint32 (info.max_framesize, buffer + 7, 3); buffer[10] = (uint8) ((info.sample_rate >> 12) & 0xff); buffer[11] = (uint8) ((info.sample_rate >> 4) & 0xff); buffer[12] = (uint8) (((info.sample_rate & 0x0f) << 4) | (channelsMinus1 << 1) | (bitsMinus1 >> 4)); buffer[13] = (FLAC__byte) (((bitsMinus1 & 0x0f) << 4) | (unsigned int) ((info.total_samples >> 32) & 0x0f)); packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4); memcpy (buffer + 18, info.md5sum, 16); const bool seekOk = output->setPosition (4); (void) seekOk; // if this fails, you've given it an output stream that can't seek! It needs // to be able to seek back to write the header jassert (seekOk); output->writeIntBigEndian (FLAC__STREAM_METADATA_STREAMINFO_LENGTH); output->write (buffer, FLAC__STREAM_METADATA_STREAMINFO_LENGTH); } //============================================================================== static FlacNamespace::FLAC__StreamEncoderWriteStatus encodeWriteCallback (const FlacNamespace::FLAC__StreamEncoder*, const FlacNamespace::FLAC__byte buffer[], size_t bytes, unsigned int /*samples*/, unsigned int /*current_frame*/, void* client_data) { using namespace FlacNamespace; return static_cast (client_data)->writeData (buffer, (int) bytes) ? FLAC__STREAM_ENCODER_WRITE_STATUS_OK : FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; } static FlacNamespace::FLAC__StreamEncoderSeekStatus encodeSeekCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64, void*) { using namespace FlacNamespace; return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; } static FlacNamespace::FLAC__StreamEncoderTellStatus encodeTellCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data) { using namespace FlacNamespace; if (client_data == nullptr) return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; *absolute_byte_offset = (FLAC__uint64) static_cast (client_data)->output->getPosition(); return FLAC__STREAM_ENCODER_TELL_STATUS_OK; } static void encodeMetadataCallback (const FlacNamespace::FLAC__StreamEncoder*, const FlacNamespace::FLAC__StreamMetadata* metadata, void* client_data) { static_cast (client_data)->writeMetaData (metadata); } bool ok; private: FlacNamespace::FLAC__StreamEncoder* encoder; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacWriter) }; //============================================================================== FlacAudioFormat::FlacAudioFormat() : AudioFormat (flacFormatName, ".flac") { } FlacAudioFormat::~FlacAudioFormat() { } Array FlacAudioFormat::getPossibleSampleRates() { const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; return Array (rates, numElementsInArray (rates)); } Array FlacAudioFormat::getPossibleBitDepths() { const int depths[] = { 16, 24 }; return Array (depths, numElementsInArray (depths)); } bool FlacAudioFormat::canDoStereo() { return true; } bool FlacAudioFormat::canDoMono() { return true; } bool FlacAudioFormat::isCompressed() { return true; } AudioFormatReader* FlacAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) { ScopedPointer r (new FlacReader (in)); if (r->sampleRate > 0) return r.release(); if (! deleteStreamIfOpeningFails) r->input = nullptr; return nullptr; } AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out, double sampleRate, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& /*metadataValues*/, int qualityOptionIndex) { if (getPossibleBitDepths().contains (bitsPerSample)) { ScopedPointer w (new FlacWriter (out, sampleRate, numberOfChannels, (uint32) bitsPerSample, qualityOptionIndex)); if (w->ok) return w.release(); } return nullptr; } StringArray FlacAudioFormat::getQualityOptions() { static const char* options[] = { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)", 0 }; return StringArray (options); } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h000066400000000000000000000047311320201440200330720ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_FLAC || defined (DOXYGEN) //============================================================================== /** Reads and writes the lossless-compression FLAC audio format. To compile this, you'll need to set the JUCE_USE_FLAC flag. @see AudioFormat */ class JUCE_API FlacAudioFormat : public AudioFormat { public: //============================================================================== FlacAudioFormat(); ~FlacAudioFormat(); //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; bool isCompressed() override; StringArray getQualityOptions() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacAudioFormat) }; #endif juce_LAMEEncoderAudioFormat.cpp000066400000000000000000000161651320201440200345230ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_LAME_AUDIO_FORMAT class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter { public: Writer (OutputStream* destStream, const String& formatName, const File& appFile, int vbr, int cbr, double sampleRate, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadata) : AudioFormatWriter (destStream, formatName, sampleRate, numberOfChannels, (unsigned int) bitsPerSample), vbrLevel (vbr), cbrBitrate (cbr), tempWav (".wav") { WavAudioFormat wavFormat; if (FileOutputStream* out = tempWav.getFile().createOutputStream()) { writer = wavFormat.createWriterFor (out, sampleRate, numChannels, bitsPerSample, metadata, 0); args.add (appFile.getFullPathName()); args.add ("--quiet"); if (cbrBitrate == 0) { args.add ("--vbr-new"); args.add ("-V"); args.add (String (vbrLevel)); } else { args.add ("--cbr"); args.add ("-b"); args.add (String (cbrBitrate)); } addMetadataArg (metadata, "id3title", "--tt"); addMetadataArg (metadata, "id3artist", "--ta"); addMetadataArg (metadata, "id3album", "--tl"); addMetadataArg (metadata, "id3comment", "--tc"); addMetadataArg (metadata, "id3date", "--ty"); addMetadataArg (metadata, "id3genre", "--tg"); addMetadataArg (metadata, "id3trackNumber", "--tn"); } } void addMetadataArg (const StringPairArray& metadata, const char* key, const char* lameFlag) { const String value (metadata.getValue (key, String())); if (value.isNotEmpty()) { args.add (lameFlag); args.add (value); } } ~Writer() { if (writer != nullptr) { writer = nullptr; if (! convertToMP3()) convertToMP3(); // try again } } bool write (const int** samplesToWrite, int numSamples) { return writer != nullptr && writer->write (samplesToWrite, numSamples); } private: int vbrLevel, cbrBitrate; TemporaryFile tempWav; ScopedPointer writer; StringArray args; bool runLameChildProcess (const TemporaryFile& tempMP3, const StringArray& processArgs) const { ChildProcess cp; if (cp.start (processArgs)) { const String childOutput (cp.readAllProcessOutput()); DBG (childOutput); (void) childOutput; cp.waitForProcessToFinish (10000); return tempMP3.getFile().getSize() > 0; } return false; } bool convertToMP3() const { TemporaryFile tempMP3 (".mp3"); StringArray args2 (args); args2.add (tempWav.getFile().getFullPathName()); args2.add (tempMP3.getFile().getFullPathName()); DBG (args2.joinIntoString (" ")); if (runLameChildProcess (tempMP3, args2)) { FileInputStream fis (tempMP3.getFile()); if (fis.openedOk() && output->writeFromInputStream (fis, -1) > 0) { output->flush(); return true; } } return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Writer) }; //============================================================================== LAMEEncoderAudioFormat::LAMEEncoderAudioFormat (const File& lameApplication) : AudioFormat ("MP3 file", ".mp3"), lameApp (lameApplication) { } LAMEEncoderAudioFormat::~LAMEEncoderAudioFormat() { } bool LAMEEncoderAudioFormat::canHandleFile (const File&) { return false; } Array LAMEEncoderAudioFormat::getPossibleSampleRates() { const int rates[] = { 32000, 44100, 48000, 0 }; return Array (rates); } Array LAMEEncoderAudioFormat::getPossibleBitDepths() { const int depths[] = { 16, 0 }; return Array (depths); } bool LAMEEncoderAudioFormat::canDoStereo() { return true; } bool LAMEEncoderAudioFormat::canDoMono() { return true; } bool LAMEEncoderAudioFormat::isCompressed() { return true; } StringArray LAMEEncoderAudioFormat::getQualityOptions() { static const char* vbrOptions[] = { "VBR quality 0 (best)", "VBR quality 1", "VBR quality 2", "VBR quality 3", "VBR quality 4 (normal)", "VBR quality 5", "VBR quality 6", "VBR quality 7", "VBR quality 8", "VBR quality 9 (smallest)", nullptr }; StringArray opts (vbrOptions); const int cbrRates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }; for (int i = 0; i < numElementsInArray (cbrRates); ++i) opts.add (String (cbrRates[i]) + " Kb/s CBR"); return opts; } AudioFormatReader* LAMEEncoderAudioFormat::createReaderFor (InputStream*, const bool) { return nullptr; } AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) { int vbr = 4; int cbr = 0; const String qual (getQualityOptions() [qualityOptionIndex]); if (qual.contains ("VBR")) vbr = qual.retainCharacters ("0123456789").getIntValue(); else cbr = qual.getIntValue(); return new Writer (streamToWriteTo, getFormatName(), lameApp, vbr, cbr, sampleRateToUse, numberOfChannels, bitsPerSample, metadataValues); } #endif juce_LAMEEncoderAudioFormat.h000066400000000000000000000051331320201440200341610ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_LAME_AUDIO_FORMAT || defined (DOXYGEN) //============================================================================== /** An AudioFormat class which can use an installed version of the LAME mp3 encoder to encode a file. This format can't read MP3s, it just writes them. Internally, the AudioFormatWriter object that is returned writes the incoming audio data to a temporary WAV file, and then when the writer is deleted, it invokes the LAME executable to convert the data to an MP3, whose data is then piped into the original OutputStream that was used when first creating the writer. @see AudioFormat */ class JUCE_API LAMEEncoderAudioFormat : public AudioFormat { public: /** Creates a LAMEEncoderAudioFormat that expects to find a working LAME executable at the location given. */ LAMEEncoderAudioFormat (const File& lameExecutableToUse); ~LAMEEncoderAudioFormat(); bool canHandleFile (const File&); Array getPossibleSampleRates(); Array getPossibleBitDepths(); bool canDoStereo(); bool canDoMono(); bool isCompressed(); StringArray getQualityOptions(); AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails); AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex); private: File lameApp; class Writer; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LAMEEncoderAudioFormat) }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp000066400000000000000000004075401320201440200331640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ /* IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing that ROLI Ltd. is in no way responsible for any patent, copyright, or other legal issues that you may suffer as a result. The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party intellectual property. If you wish to use it, please seek your own independent advice about the legality of doing so. If you are not willing to accept full responsibility for the consequences of using this code, then do not enable the JUCE_USE_MP3AUDIOFORMAT setting. */ #if JUCE_USE_MP3AUDIOFORMAT namespace MP3Decoder { struct AllocationTable { int16 bits, d; }; const struct AllocationTable allocTable0[] = { {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767} }; const struct AllocationTable allocTable1[] = { {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {3, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767} }; const struct AllocationTable allocTable2[] = { {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63} }; const struct AllocationTable allocTable3[] = { {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63} }; const struct AllocationTable allocTable4[] = { {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9}, {2, 0}, {5, 3}, {7, 5}, {10, 9} }; struct BandInfoStruct { int16 longIndex[23]; int16 longDiff[22]; int16 shortIndex[14]; int16 shortDiff[13]; }; const BandInfoStruct bandInfo[9] = { { {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576}, {4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158}, {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 40 * 3, 52 * 3, 66 * 3, 84 * 3, 106 * 3, 136 * 3, 192 * 3}, {4, 4, 4, 4, 6, 8, 10, 12, 14, 18, 22, 30, 56} }, { {0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576}, {4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 10, 12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192}, {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 28 * 3, 38 * 3, 50 * 3, 64 * 3, 80 * 3, 100 * 3, 126 * 3, 192 * 3}, {4, 4, 4, 4, 6, 6, 10, 12, 14, 16, 20, 26, 66} }, { {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576}, {4, 4, 4, 4, 4, 4, 6, 6, 8, 10, 12, 16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26}, {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 42 * 3, 58 * 3, 78 * 3, 104 * 3, 138 * 3, 180 * 3, 192 * 3}, {4, 4, 4, 4, 6, 8, 12, 16, 20, 26, 34, 42, 12} }, { {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54 }, {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 24 * 3, 32 * 3, 42 * 3, 56 * 3, 74 * 3, 100 * 3, 132 * 3, 174 * 3, 192 * 3}, {4, 4, 4, 6, 6, 8, 10, 14, 18, 26, 32, 42, 18 } }, { {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 332, 394, 464, 540, 576}, {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 18, 22, 26, 32, 38, 46, 54, 62, 70, 76, 36 }, {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3, 104 * 3, 136 * 3, 180 * 3, 192 * 3}, {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 32, 44, 12 } }, { {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54 }, {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3, 104 * 3, 134 * 3, 174 * 3, 192 * 3}, {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18 } }, { {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576}, {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18} }, { {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576}, {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18} }, { {0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576}, {12, 12, 12, 12, 12, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 76, 90, 2, 2, 2, 2, 2}, {0, 24, 48, 72, 108, 156, 216, 288, 372, 480, 486, 492, 498, 576}, {8, 8, 8, 12, 16, 20, 24, 28, 36, 2, 2, 2, 26} } }; const double decodeWindow[] = { 0.000000000, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000030518, -0.000030518, -0.000030518, -0.000030518, -0.000045776, -0.000045776, -0.000061035, -0.000061035, -0.000076294, -0.000076294, -0.000091553, -0.000106812, -0.000106812, -0.000122070, -0.000137329, -0.000152588, -0.000167847, -0.000198364, -0.000213623, -0.000244141, -0.000259399, -0.000289917, -0.000320435, -0.000366211, -0.000396729, -0.000442505, -0.000473022, -0.000534058, -0.000579834, -0.000625610, -0.000686646, -0.000747681, -0.000808716, -0.000885010, -0.000961304, -0.001037598, -0.001113892, -0.001205444, -0.001296997, -0.001388550, -0.001480103, -0.001586914, -0.001693726, -0.001785278, -0.001907349, -0.002014160, -0.002120972, -0.002243042, -0.002349854, -0.002456665, -0.002578735, -0.002685547, -0.002792358, -0.002899170, -0.002990723, -0.003082275, -0.003173828, -0.003250122, -0.003326416, -0.003387451, -0.003433228, -0.003463745, -0.003479004, -0.003479004, -0.003463745, -0.003417969, -0.003372192, -0.003280640, -0.003173828, -0.003051758, -0.002883911, -0.002700806, -0.002487183, -0.002227783, -0.001937866, -0.001617432, -0.001266479, -0.000869751, -0.000442505, 0.000030518, 0.000549316, 0.001098633, 0.001693726, 0.002334595, 0.003005981, 0.003723145, 0.004486084, 0.005294800, 0.006118774, 0.007003784, 0.007919312, 0.008865356, 0.009841919, 0.010848999, 0.011886597, 0.012939453, 0.014022827, 0.015121460, 0.016235352, 0.017349243, 0.018463135, 0.019577026, 0.020690918, 0.021789551, 0.022857666, 0.023910522, 0.024932861, 0.025909424, 0.026840210, 0.027725220, 0.028533936, 0.029281616, 0.029937744, 0.030532837, 0.031005859, 0.031387329, 0.031661987, 0.031814575, 0.031845093, 0.031738281, 0.031478882, 0.031082153, 0.030517578, 0.029785156, 0.028884888, 0.027801514, 0.026535034, 0.025085449, 0.023422241, 0.021575928, 0.019531250, 0.017257690, 0.014801025, 0.012115479, 0.009231567, 0.006134033, 0.002822876, -0.000686646, -0.004394531, -0.008316040, -0.012420654, -0.016708374, -0.021179199, -0.025817871, -0.030609131, -0.035552979, -0.040634155, -0.045837402, -0.051132202, -0.056533813, -0.061996460, -0.067520142, -0.073059082, -0.078628540, -0.084182739, -0.089706421, -0.095169067, -0.100540161, -0.105819702, -0.110946655, -0.115921021, -0.120697021, -0.125259399, -0.129562378, -0.133590698, -0.137298584, -0.140670776, -0.143676758, -0.146255493, -0.148422241, -0.150115967, -0.151306152, -0.151962280, -0.152069092, -0.151596069, -0.150497437, -0.148773193, -0.146362305, -0.143264771, -0.139450073, -0.134887695, -0.129577637, -0.123474121, -0.116577148, -0.108856201, -0.100311279, -0.090927124, -0.080688477, -0.069595337, -0.057617187, -0.044784546, -0.031082153, -0.016510010, -0.001068115, 0.015228271, 0.032379150, 0.050354004, 0.069168091, 0.088775635, 0.109161377, 0.130310059, 0.152206421, 0.174789429, 0.198059082, 0.221984863, 0.246505737, 0.271591187, 0.297210693, 0.323318481, 0.349868774, 0.376800537, 0.404083252, 0.431655884, 0.459472656, 0.487472534, 0.515609741, 0.543823242, 0.572036743, 0.600219727, 0.628295898, 0.656219482, 0.683914185, 0.711318970, 0.738372803, 0.765029907, 0.791213989, 0.816864014, 0.841949463, 0.866363525, 0.890090942, 0.913055420, 0.935195923, 0.956481934, 0.976852417, 0.996246338, 1.014617920, 1.031936646, 1.048156738, 1.063217163, 1.077117920, 1.089782715, 1.101211548, 1.111373901, 1.120223999, 1.127746582, 1.133926392, 1.138763428, 1.142211914, 1.144287109, 1.144989014 }; const int16 huffmanTab0[] = { 0 }; const int16 huffmanTab1[] = { -5,-3,-1,17,1,16,0 }; const int16 huffmanTab2[] = { -15,-11,-9,-5,-3,-1,34,2,18,-1,33,32,17,-1,1,16,0 }; const int16 huffmanTab3[] = { -13,-11,-9,-5,-3,-1,34,2,18,-1,33,32,16,17,-1,1,0 }; const int16 huffmanTab5[] = { -29,-25,-23,-15,-7,-5,-3,-1,51,35,50,49,-3,-1,19,3,-1,48,34,-3,-1,18,33,-1,2,32,17,-1,1,16,0 }; const int16 huffmanTab6[] = { -25,-19,-13,-9,-5,-3,-1,51,3,35,-1,50,48,-1,19,49,-3,-1,34,2,18,-3,-1,33,32,1,-1,17,-1,16,0 }; const int16 huffmanTab7[] = { -69,-65,-57,-39,-29,-17,-11,-7,-3,-1,85,69,-1,84,83,-1,53,68,-3,-1,37,82,21,-5,-1,81,-1,5,52,-1,80,-1,67,51, -5,-3,-1,36,66,20,-1,65,64,-11,-7,-3,-1,4,35,-1,50,3,-1,19,49,-3,-1,48,34,18,-5,-1,33,-1,2,32,17,-1,1,16,0 }; const int16 huffmanTab8[] = { -65,-63,-59,-45,-31,-19,-13,-7,-5,-3,-1,85,84,69,83,-3,-1,53,68,37,-3,-1,82,5,21,-5,-1,81,-1,52,67,-3,-1,80, 51,36,-5,-3,-1,66,20,65,-3,-1,4,64,-1,35,50,-9,-7,-3,-1,19,49,-1,3,48,34,-1,2,32,-1,18,33,17,-3,-1,1,16,0 }; const int16 huffmanTab9[] = { -63,-53,-41,-29,-19,-11,-5,-3,-1,85,69,53,-1,83,-1,84,5,-3,-1,68,37,-1,82,21,-3,-1,81,52,-1,67,-1,80,4,-7,-3, -1,36,66,-1,51,64,-1,20,65,-5,-3,-1,35,50,19,-1,49,-1,3,48,-5,-3,-1,34,2,18,-1,33,32,-3,-1,17,1,-1,16,0 }; const int16 huffmanTab10[] = { -125,-121,-111,-83,-55,-35,-21,-13,-7,-3,-1,119,103,-1,118,87,-3,-1,117,102,71,-3,-1,116,86,-1,101,55,-9,-3, -1,115,70,-3,-1,85,84,99,-1,39,114,-11,-5,-3,-1,100,7,112,-1,98,-1,69,53,-5,-1,6,-1,83,68,23,-17,-5,-1,113, -1,54,38,-5,-3,-1,37,82,21,-1,81,-1,52,67,-3,-1,22,97,-1,96,-1,5,80,-19,-11,-7,-3,-1,36,66,-1,51,4,-1,20, 65,-3,-1,64,35,-1,50,3,-3,-1,19,49,-1,48,34,-7,-3,-1,18,33,-1,2,32,17,-1,1,16,0 }; const int16 huffmanTab11[] = { -121,-113,-89,-59,-43,-27,-17,-7,-3,-1,119,103,-1,118,117,-3,-1,102,71,-1,116,-1,87,85,-5,-3,-1,86,101,55, -1,115,70,-9,-7,-3,-1,69,84,-1,53,83,39,-1,114,-1,100,7,-5,-1,113,-1,23,112,-3,-1,54,99,-1,96,-1,68,37,-13, -7,-5,-3,-1,82,5,21,98,-3,-1,38,6,22,-5,-1,97,-1,81,52,-5,-1,80,-1,67,51,-1,36,66,-15,-11,-7,-3,-1,20,65, -1,4,64,-1,35,50,-1,19,49,-5,-3,-1,3,48,34,33,-5,-1,18,-1,2,32,17,-3,-1,1,16,0 }; const int16 huffmanTab12[] = { -115,-99,-73,-45,-27,-17,-9,-5,-3,-1,119,103,118,-1,87,117,-3,-1,102,71,-1,116,101,-3,-1,86,55,-3,-1,115, 85,39,-7,-3,-1,114,70,-1,100,23,-5,-1,113,-1,7,112,-1,54,99,-13,-9,-3,-1,69,84,-1,68,-1,6,5,-1,38,98,-5, -1,97,-1,22,96,-3,-1,53,83,-1,37,82,-17,-7,-3,-1,21,81,-1,52,67,-5,-3,-1,80,4,36,-1,66,20,-3,-1,51,65,-1, 35,50,-11,-7,-5,-3,-1,64,3,48,19,-1,49,34,-1,18,33,-7,-5,-3,-1,2,32,0,17,-1,1,16 }; const int16 huffmanTab13[] = { -509,-503,-475,-405,-333,-265,-205,-153,-115,-83,-53,-35,-21,-13,-9,-7,-5,-3,-1,254,252,253,237,255,-1,239,223, -3,-1,238,207,-1,222,191,-9,-3,-1,251,206,-1,220,-1,175,233,-1,236,221,-9,-5,-3,-1,250,205,190,-1,235,159,-3, -1,249,234,-1,189,219,-17,-9,-3,-1,143,248,-1,204,-1,174,158,-5,-1,142,-1,127,126,247,-5,-1,218,-1,173,188,-3, -1,203,246,111,-15,-7,-3,-1,232,95,-1,157,217,-3,-1,245,231,-1,172,187,-9,-3,-1,79,244,-3,-1,202,230,243,-1, 63,-1,141,216,-21,-9,-3,-1,47,242,-3,-1,110,156,15,-5,-3,-1,201,94,171,-3,-1,125,215,78,-11,-5,-3,-1,200,214, 62,-1,185,-1,155,170,-1,31,241,-23,-13,-5,-1,240,-1,186,229,-3,-1,228,140,-1,109,227,-5,-1,226,-1,46,14,-1,30, 225,-15,-7,-3,-1,224,93,-1,213,124,-3,-1,199,77,-1,139,184,-7,-3,-1,212,154,-1,169,108,-1,198,61,-37,-21,-9,-5, -3,-1,211,123,45,-1,210,29,-5,-1,183,-1,92,197,-3,-1,153,122,195,-7,-5,-3,-1,167,151,75,209,-3,-1,13,208,-1, 138,168,-11,-7,-3,-1,76,196,-1,107,182,-1,60,44,-3,-1,194,91,-3,-1,181,137,28,-43,-23,-11,-5,-1,193,-1,152,12, -1,192,-1,180,106,-5,-3,-1,166,121,59,-1,179,-1,136,90,-11,-5,-1,43,-1,165,105,-1,164,-1,120,135,-5,-1,148,-1, 119,118,178,-11,-3,-1,27,177,-3,-1,11,176,-1,150,74,-7,-3,-1,58,163,-1,89,149,-1,42,162,-47,-23,-9,-3,-1,26, 161,-3,-1,10,104,160,-5,-3,-1,134,73,147,-3,-1,57,88,-1,133,103,-9,-3,-1,41,146,-3,-1,87,117,56,-5,-1,131,-1, 102,71,-3,-1,116,86,-1,101,115,-11,-3,-1,25,145,-3,-1,9,144,-1,72,132,-7,-5,-1,114,-1,70,100,40,-1,130,24,-41, -27,-11,-5,-3,-1,55,39,23,-1,113,-1,85,7,-7,-3,-1,112,54,-1,99,69,-3,-1,84,38,-1,98,53,-5,-1,129,-1,8,128,-3, -1,22,97,-1,6,96,-13,-9,-5,-3,-1,83,68,37,-1,82,5,-1,21,81,-7,-3,-1,52,67,-1,80,36,-3,-1,66,51,20,-19,-11, -5,-1,65,-1,4,64,-3,-1,35,50,19,-3,-1,49,3,-1,48,34,-3,-1,18,33,-1,2,32,-3,-1,17,1,16,0 }; const int16 huffmanTab15[] = { -495,-445,-355,-263,-183,-115,-77,-43,-27,-13,-7,-3,-1,255,239,-1,254,223,-1,238,-1,253,207,-7,-3,-1,252,222,-1, 237,191,-1,251,-1,206,236,-7,-3,-1,221,175,-1,250,190,-3,-1,235,205,-1,220,159,-15,-7,-3,-1,249,234,-1,189,219, -3,-1,143,248,-1,204,158,-7,-3,-1,233,127,-1,247,173,-3,-1,218,188,-1,111,-1,174,15,-19,-11,-3,-1,203,246, -3,-1,142,232,-1,95,157,-3,-1,245,126,-1,231,172,-9,-3,-1,202,187,-3,-1,217,141,79,-3,-1,244,63,-1,243,216, -33,-17,-9,-3,-1,230,47,-1,242,-1,110,240,-3,-1,31,241,-1,156,201,-7,-3,-1,94,171,-1,186,229,-3,-1,125,215, -1,78,228,-15,-7,-3,-1,140,200,-1,62,109,-3,-1,214,227,-1,155,185,-7,-3,-1,46,170,-1,226,30,-5,-1,225,-1,14, 224,-1,93,213,-45,-25,-13,-7,-3,-1,124,199,-1,77,139,-1,212,-1,184,154,-7,-3,-1,169,108,-1,198,61,-1,211,210, -9,-5,-3,-1,45,13,29,-1,123,183,-5,-1,209,-1,92,208,-1,197,138,-17,-7,-3,-1,168,76,-1,196,107,-5,-1,182,-1, 153,12,-1,60,195,-9,-3,-1,122,167,-1,166,-1,192,11,-1,194,-1,44,91,-55,-29,-15,-7,-3,-1,181,28,-1,137,152,-3, -1,193,75,-1,180,106,-5,-3,-1,59,121,179,-3,-1,151,136,-1,43,90,-11,-5,-1,178,-1,165,27,-1,177,-1,176,105,-7, -3,-1,150,74,-1,164,120,-3,-1,135,58,163,-17,-7,-3,-1,89,149,-1,42,162,-3,-1,26,161,-3,-1,10,160,104,-7,-3, -1,134,73,-1,148,57,-5,-1,147,-1,119,9,-1,88,133,-53,-29,-13,-7,-3,-1,41,103,-1,118,146,-1,145,-1,25,144,-7, -3,-1,72,132,-1,87,117,-3,-1,56,131,-1,102,71,-7,-3,-1,40,130,-1,24,129,-7,-3,-1,116,8,-1,128,86,-3,-1,101, 55,-1,115,70,-17,-7,-3,-1,39,114,-1,100,23,-3,-1,85,113,-3,-1,7,112,54,-7,-3,-1,99,69,-1,84,38,-3,-1,98,22, -3,-1,6,96,53,-33,-19,-9,-5,-1,97,-1,83,68,-1,37,82,-3,-1,21,81,-3,-1,5,80,52,-7,-3,-1,67,36,-1,66,51,-1, 65,-1,20,4,-9,-3,-1,35,50,-3,-1,64,3,19,-3,-1,49,48,34,-9,-7,-3,-1,18,33,-1,2,32,17,-3,-1,1,16,0 }; const int16 huffmanTab16[] = { -509,-503,-461,-323,-103,-37,-27,-15,-7,-3,-1,239,254,-1,223,253,-3,-1,207,252,-1,191,251,-5,-1,175,-1,250,159, -3,-1,249,248,143,-7,-3,-1,127,247,-1,111,246,255,-9,-5,-3,-1,95,245,79,-1,244,243,-53,-1,240,-1,63,-29,-19, -13,-7,-5,-1,206,-1,236,221,222,-1,233,-1,234,217,-1,238,-1,237,235,-3,-1,190,205,-3,-1,220,219,174,-11,-5, -1,204,-1,173,218,-3,-1,126,172,202,-5,-3,-1,201,125,94,189,242,-93,-5,-3,-1,47,15,31,-1,241,-49,-25,-13, -5,-1,158,-1,188,203,-3,-1,142,232,-1,157,231,-7,-3,-1,187,141,-1,216,110,-1,230,156,-13,-7,-3,-1,171,186, -1,229,215,-1,78,-1,228,140,-3,-1,200,62,-1,109,-1,214,155,-19,-11,-5,-3,-1,185,170,225,-1,212,-1,184,169, -5,-1,123,-1,183,208,227,-7,-3,-1,14,224,-1,93,213,-3,-1,124,199,-1,77,139,-75,-45,-27,-13,-7,-3,-1,154, 108,-1,198,61,-3,-1,92,197,13,-7,-3,-1,138,168,-1,153,76,-3,-1,182,122,60,-11,-5,-3,-1,91,137,28,-1,192,-1, 152,121,-1,226,-1,46,30,-15,-7,-3,-1,211,45,-1,210,209,-5,-1,59,-1,151,136,29,-7,-3,-1,196,107,-1,195,167,-1, 44,-1,194,181,-23,-13,-7,-3,-1,193,12,-1,75,180,-3,-1,106,166,179,-5,-3,-1,90,165,43,-1,178,27,-13,-5,-1,177, -1,11,176,-3,-1,105,150,-1,74,164,-5,-3,-1,120,135,163,-3,-1,58,89,42,-97,-57,-33,-19,-11,-5,-3,-1,149,104,161, -3,-1,134,119,148,-5,-3,-1,73,87,103,162,-5,-1,26,-1,10,160,-3,-1,57,147,-1,88,133,-9,-3,-1,41,146,-3,-1,118, 9,25,-5,-1,145,-1,144,72,-3,-1,132,117,-1,56,131,-21,-11,-5,-3,-1,102,40,130,-3,-1,71,116,24,-3,-1,129,128,-3, -1,8,86,55,-9,-5,-1,115,-1,101,70,-1,39,114,-5,-3,-1,100,85,7,23,-23,-13,-5,-1,113,-1,112,54,-3,-1,99,69,-1, 84,38,-3,-1,98,22,-1,97,-1,6,96,-9,-5,-1,83,-1,53,68,-1,37,82,-1,81,-1,21,5,-33,-23,-13,-7,-3,-1,52,67,-1,80, 36,-3,-1,66,51,20,-5,-1,65,-1,4,64,-1,35,50,-3,-1,19,49,-3,-1,3,48,34,-3,-1,18,33,-1,2,32,-3,-1,17,1,16,0 }; const int16 huffmanTab24[] = { -451,-117,-43,-25,-15,-7,-3,-1,239,254,-1,223,253,-3,-1,207,252,-1,191,251,-5,-1,250,-1,175,159,-1,249,248,-9, -5,-3,-1,143,127,247,-1,111,246,-3,-1,95,245,-1,79,244,-71,-7,-3,-1,63,243,-1,47,242,-5,-1,241,-1,31,240,-25,-9, -1,15,-3,-1,238,222,-1,237,206,-7,-3,-1,236,221,-1,190,235,-3,-1,205,220,-1,174,234,-15,-7,-3,-1,189,219,-1,204, 158,-3,-1,233,173,-1,218,188,-7,-3,-1,203,142,-1,232,157,-3,-1,217,126,-1,231,172,255,-235,-143,-77,-45,-25,-15, -7,-3,-1,202,187,-1,141,216,-5,-3,-1,14,224,13,230,-5,-3,-1,110,156,201,-1,94,186,-9,-5,-1,229,-1,171,125,-1,215, 228,-3,-1,140,200,-3,-1,78,46,62,-15,-7,-3,-1,109,214,-1,227,155,-3,-1,185,170,-1,226,30,-7,-3,-1,225,93,-1,213,124, -3,-1,199,77,-1,139,184,-31,-15,-7,-3,-1,212,154,-1,169,108,-3,-1,198,61,-1,211,45,-7,-3,-1,210,29,-1,123,183,-3,-1, 209,92,-1,197,138,-17,-7,-3,-1,168,153,-1,76,196,-3,-1,107,182,-3,-1,208,12,60,-7,-3,-1,195,122,-1,167,44,-3,-1,194, 91,-1,181,28,-57,-35,-19,-7,-3,-1,137,152,-1,193,75,-5,-3,-1,192,11,59,-3,-1,176,10,26,-5,-1,180,-1,106,166,-3,-1,121, 151,-3,-1,160,9,144,-9,-3,-1,179,136,-3,-1,43,90,178,-7,-3,-1,165,27,-1,177,105,-1,150,164,-17,-9,-5,-3,-1,74,120,135, -1,58,163,-3,-1,89,149,-1,42,162,-7,-3,-1,161,104,-1,134,119,-3,-1,73,148,-1,57,147,-63,-31,-15,-7,-3,-1,88,133,-1,41, 103,-3,-1,118,146,-1,25,145,-7,-3,-1,72,132,-1,87,117,-3,-1,56,131,-1,102,40,-17,-7,-3,-1,130,24,-1,71,116,-5,-1,129, -1,8,128,-1,86,101,-7,-5,-1,23,-1,7,112,115,-3,-1,55,39,114,-15,-7,-3,-1,70,100,-1,85,113,-3,-1,54,99,-1,69,84,-7,-3, -1,38,98,-1,22,97,-5,-3,-1,6,96,53,-1,83,68,-51,-37,-23,-15,-9,-3,-1,37,82,-1,21,-1,5,80,-1,81,-1,52,67,-3,-1,36,66, -1,51,20,-9,-5,-1,65,-1,4,64,-1,35,50,-1,19,49,-7,-5,-3,-1,3,48,34,18,-1,33,-1,2,32,-3,-1,17,1,-1,16,0 }; struct BitsToTableMap { uint32 bits; const int16* table; }; const BitsToTableMap huffmanTables1[] = { { 0, huffmanTab0 }, { 0, huffmanTab1 }, { 0, huffmanTab2 }, { 0, huffmanTab3 }, { 0, huffmanTab0 }, { 0, huffmanTab5 }, { 0, huffmanTab6 }, { 0, huffmanTab7 }, { 0, huffmanTab8 }, { 0, huffmanTab9 }, { 0, huffmanTab10 }, { 0, huffmanTab11 }, { 0, huffmanTab12 }, { 0, huffmanTab13 }, { 0, huffmanTab0 }, { 0, huffmanTab15 }, { 1, huffmanTab16 }, { 2, huffmanTab16 }, { 3, huffmanTab16 }, { 4, huffmanTab16 }, { 6, huffmanTab16 }, { 8, huffmanTab16 }, { 10, huffmanTab16 }, { 13, huffmanTab16 }, { 4, huffmanTab24 }, { 5, huffmanTab24 }, { 6, huffmanTab24 }, { 7, huffmanTab24 }, { 8, huffmanTab24 }, { 9, huffmanTab24 }, { 11, huffmanTab24 }, { 13, huffmanTab24 } }; const int16 huffmanTabC0[] = { -29,-21,-13,-7,-3,-1,11,15,-1,13,14,-3,-1,7,5,9,-3,-1,6,3,-1,10,12,-3,-1,2,1,-1,4,8,0 }; const int16 huffmanTabC1[] = { -15,-7,-3,-1,15,14,-1,13,12,-3,-1,11,10,-1,9,8,-7,-3,-1,7,6,-1,5,4,-3,-1,3,2,-1,1,0 }; const BitsToTableMap huffmanTables2[] = { { 0, huffmanTabC0 }, { 0, huffmanTabC1 } }; //============================================================================== struct VBRTagData { bool read (const uint8* data) noexcept { flags = 0; const int layer = (data[1] >> 1) & 3; if (layer != 1) return false; const int type = (data[1] >> 3) & 1; const int sampleRateIndex = (data[2] >> 2) & 3; const int mode = (data[3] >> 6) & 3; static const int bitRates[3][16] = { { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // MPEG2 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, // MPEG1 { 0, 8, 16, 24, 32, 40, 48, 56, 64, -1, -1, -1, -1, -1, -1, -1 }, // MPEG 2.5 }; const int bitrate = bitRates[type][((data[2] >> 4) & 15)]; const int sampleRates[3][4] = { { 22050, 24000, 16000, -1 }, // MPEG2 { 44100, 48000, 32000, -1 }, // MPEG1 { 11025, 12000, 8000, -1 }, // MPEG2.5 }; if ((data[1] >> 4) == 0xe) sampleRate = sampleRates[2][sampleRateIndex]; else sampleRate = sampleRates[type][sampleRateIndex]; data += type != 0 ? (mode != 3 ? (32 + 4) : (17 + 4)) : (mode != 3 ? (17 + 4) : (9 + 4)); if (! isVbrTag (data)) return false; data += 4; flags = ByteOrder::bigEndianInt (data); data += 4; if (flags & 1) { frames = ByteOrder::bigEndianInt (data); data += 4; } if (flags & 2) { bytes = ByteOrder::bigEndianInt (data); data += 4; } if (flags & 4) { for (int i = 0; i < 100; ++i) toc[i] = data[i]; data += 100; } vbrScale = -1; if (flags & 8) vbrScale = (int) ByteOrder::bigEndianInt (data); headersize = ((type + 1) * 72000 * bitrate) / sampleRate; return true; } uint8 toc[100]; int sampleRate, vbrScale, headersize; unsigned int flags, frames, bytes; private: static bool isVbrTag (const uint8* const d) noexcept { return (d[0] == 'X' && d[1] == 'i' && d[2] == 'n' && d[3] == 'g') || (d[0] == 'I' && d[1] == 'n' && d[2] == 'f' && d[3] == 'o'); } }; //============================================================================== struct MP3Frame { MP3Frame() { zeromem (this, sizeof (MP3Frame)); single = -1; } void selectLayer2Table() { static const int translate[3][2][16] = { { { 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, { 0, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 } }, { { 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, { { 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, { 0, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 } } }; static const AllocationTable* const tables[] = { allocTable0, allocTable1, allocTable2, allocTable3, allocTable4 }; static const int limits[] = { 27, 30, 8, 12, 30 }; const int index = lsf ? 4 : translate[sampleRateIndex][2 - numChannels][bitrateIndex]; layer2SubBandLimit = limits [index]; allocationTable = tables [index]; } int getFrequency() const noexcept { const int frequencies[] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000 }; return frequencies [sampleRateIndex]; } void decodeHeader (const uint32 header) { jassert (((header >> 10) & 3) != 3); mpeg25 = (header & (1 << 20)) == 0; lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); layer = 4 - ((header >> 17) & 3); sampleRateIndex = mpeg25 ? (6 + ((header >> 10) & 3)) : ((int) ((header >> 10) & 3) + (lsf * 3)); crc16FollowsHeader = ((header >> 16) & 1) == 0; bitrateIndex = (header >> 12) & 15; padding = (header >> 9) & 1; mode = (header >> 6) & 3; modeExt = (header >> 4) & 3; //extension = (header >> 8) & 1; //copyright = (header >> 3) & 1; //original = (header >> 2) & 1; //emphasis = header & 3; numChannels = (mode == 3) ? 1 : 2; static const int frameSizes [2][3][16] = { { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 } }, { { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 } } }; if (bitrateIndex == 0) { jassertfalse; // This means the file is using "free format". Apparently very few decoders // support this mode, and this one certainly doesn't handle it correctly! frameSize = 0; } else { switch (layer) { case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break; case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break; case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break; default: break; } } } int layer, frameSize, numChannels, single; int lsf; // 0 = mpeg-1, 1 = mpeg-2/LSF bool mpeg25; // true = mpeg-2.5, false = mpeg-1/2 bool crc16FollowsHeader; int bitrateIndex, sampleRateIndex, padding; int mode, modeExt, layer2SubBandLimit; enum { downSampleLimit = 32 }; const AllocationTable* allocationTable; }; //============================================================================== struct Constants { Constants() { cosTables[0] = cos64; cosTables[1] = cos32; cosTables[2] = cos16; cosTables[3] = cos8; cosTables[4] = cos4; initDecodeTables(); initLayer2Tables(); initLayer3Tables(); } const uint8* getGroupTable (const int16 d1, const uint32 index) const noexcept { switch (d1) { case 3: return &group3tab [3 * jmin (index, 3u * 3u * 3u)]; case 5: return &group5tab [3 * jmin (index, 5u * 5u * 5u)]; case 9: return &group9tab [3 * jmin (index, 9u * 9u * 9u)]; default: break; } static const uint8 dummy[] = { 0, 0, 0 }; return dummy; } float muls[27][64]; float nToThe4Over3[8207]; float antiAliasingCa[8], antiAliasingCs[8]; float win[4][36]; float win1[4][36]; float powToGains[256 + 118 + 4]; int longLimit[9][23]; int shortLimit[9][14]; float tan1_1[16], tan2_1[16], tan1_2[16], tan2_2[16]; float pow1_1[2][16], pow2_1[2][16], pow1_2[2][16], pow2_2[2][16]; int* map[9][3]; int* mapEnd[9][3]; uint32 nLength2[512]; uint32 iLength2[256]; float decodeWin[512 + 32]; float* cosTables[5]; private: int mapbuf0[9][152]; int mapbuf1[9][156]; int mapbuf2[9][44]; float cos64[16], cos32[8], cos16[4], cos8[2], cos4[1]; uint8 group3tab [32 * 3]; uint8 group5tab [128 * 3]; uint8 group9tab [1024 * 3]; void initDecodeTables() { int i, j, scaleval = -1; float* table = decodeWin; for (i = 0; i < 5; ++i) { int kr = 0x10 >> i; int divv = 0x40 >> i; float* costab = cosTables[i]; for (int k = 0; k < kr; ++k) costab[k] = (float) (1.0 / (2.0 * std::cos (double_Pi * (k * 2 + 1) / divv))); } for (i = 0, j = 0; i < 256; ++i, ++j, table += 32) { if (table < decodeWin + 512 + 16) table[16] = table[0] = (float) (decodeWindow[j] * scaleval); if (i % 32 == 31) table -= 1023; if (i % 64 == 63) scaleval = -scaleval; } for (; i < 512; ++i, --j, table += 32) { if (table < decodeWin + 512 + 16) table[16] = table[0] = (float) (decodeWindow[j] * scaleval); if (i % 32 == 31) table -= 1023; if (i % 64 == 63) scaleval = -scaleval; } } void initLayer2Tables() { static const uint8 base[3][9] = { { 1, 0, 2 }, { 17, 18, 0, 19, 20 }, { 21, 1, 22, 23, 0, 24, 25, 2, 26 } }; static const int tableLengths[] = { 3, 5, 9 }; static uint8* tables[] = { group3tab, group5tab, group9tab }; for (int i = 0; i < 3; ++i) { uint8* table = tables[i]; const int len = tableLengths[i]; for (int j = 0; j < len; ++j) for (int k = 0; k < len; ++k) for (int l = 0; l < len; ++l) { *table++ = base[i][l]; *table++ = base[i][k]; *table++ = base[i][j]; } } for (int k = 0; k < 27; ++k) { static const double multipliers[] = { 0, -2.0 / 3.0, 2.0 / 3.0, 2.0 / 7.0, 2.0 / 15.0, 2.0 / 31.0, 2.0 / 63.0, 2.0 / 127.0, 2.0 / 255.0, 2.0 / 511.0, 2.0 / 1023.0, 2.0 / 2047.0, 2.0 / 4095.0, 2.0 / 8191.0, 2.0 / 16383.0, 2.0 / 32767.0, 2.0 / 65535.0, -4.0 / 5.0, -2.0 / 5.0, 2.0 / 5.0, 4.0 / 5.0, -8.0 / 9.0, -4.0 / 9.0, -2.0 / 9.0, 2.0 / 9.0, 4.0 / 9.0, 8.0 / 9.0 }; float* table = muls[k]; for (int j = 3, i = 0; i < 63; ++i, --j) *table++ = (float) (multipliers[k] * pow (2.0, j / 3.0)); *table++ = 0; } } void initLayer3Tables() { int i, j; for (i = -256; i < 118 + 4; ++i) powToGains[i + 256] = (float) pow (2.0, -0.25 * (i + 210)); for (i = 0; i < 8207; ++i) nToThe4Over3[i] = (float) pow ((double) i, 4.0 / 3.0); for (i = 0; i < 8; ++i) { static double Ci[] = { -0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037 }; const double sq = sqrt (1.0 + Ci[i] * Ci[i]); antiAliasingCs[i] = (float) (1.0 / sq); antiAliasingCa[i] = (float) (Ci[i] / sq); } for (i = 0; i < 18; ++i) { win[0][i] = win[1][i] = (float) (0.5 * std::sin (double_Pi / 72.0 * (2 * i + 1)) / std::cos (double_Pi * (2 * i + 19) / 72.0)); win[0][i + 18] = win[3][i + 18] = (float) (0.5 * std::sin (double_Pi / 72.0 * (2 * (i + 18) + 1)) / std::cos (double_Pi * (2 * (i + 18) + 19) / 72.0)); } const double piOver72 = double_Pi; for (i = 0; i < 6; ++i) { win[1][i + 18] = (float) (0.5 / std::cos (piOver72 * (2 * (i + 18) + 19))); win[3][i + 12] = (float) (0.5 / std::cos (piOver72 * (2 * (i + 12) + 19))); win[1][i + 24] = (float) (0.5 * std::sin (double_Pi / 24.0 * (2 * i + 13)) / std::cos (piOver72 * (2 * (i + 24) + 19))); win[1][i + 30] = win[3][i] = 0; win[3][i + 6] = (float) (0.5 * std::sin (double_Pi / 24.0 * (2 * i + 1)) / std::cos (piOver72 * (2 * (i + 6) + 19))); } for (i = 0; i < 12; ++i) win[2][i] = (float) (0.5 * std::sin (double_Pi / 24.0 * (2 * i + 1)) / std::cos (double_Pi * (2 * i + 7) / 24.0)); for (j = 0; j < 4; ++j) { static const int len[4] = { 36, 36, 12, 36 }; for (i = 0; i < len[j]; i += 2) win1[j][i] = win[j][i]; for (i = 1; i < len[j]; i += 2) win1[j][i] = -win[j][i]; } const double sqrt2 = 1.41421356237309504880168872420969808; for (i = 0; i < 16; ++i) { const double t = std::tan (i * double_Pi / 12.0); tan1_1[i] = (float) (t / (1.0 + t)); tan2_1[i] = (float) (1.0 / (1.0 + t)); tan1_2[i] = (float) (sqrt2 * t / (1.0 + t)); tan2_2[i] = (float) (sqrt2 / (1.0 + t)); for (j = 0; j < 2; ++j) { double p1 = 1.0, p2 = 1.0; if (i > 0) { const double base = pow (2.0, -0.25 * (j + 1)); if (i & 1) p1 = pow (base, (i + 1) * 0.5); else p2 = pow (base, i * 0.5); } pow1_1[j][i] = (float) p1; pow2_1[j][i] = (float) p2; pow1_2[j][i] = (float) (sqrt2 * p1); pow2_2[j][i] = (float) (sqrt2 * p2); } } for (j = 0; j < 9; ++j) { const BandInfoStruct& bi = bandInfo[j]; int cb; int* mp = map[j][0] = mapbuf0[j]; const int16* bdf = bi.longDiff; for (i = 0, cb = 0; cb < 8; ++cb, i += *bdf++) { *mp++ = (*bdf) >> 1; *mp++ = i; *mp++ = 3; *mp++ = cb; } bdf = bi.shortDiff + 3; for (cb = 3; cb < 13; ++cb) { const int l = (*bdf++) >> 1; for (int lwin = 0; lwin < 3; ++lwin) { *mp++ = l; *mp++ = i + lwin; *mp++ = lwin; *mp++ = cb; } i += 6 * l; } mapEnd[j][0] = mp; mp = map[j][1] = mapbuf1[j]; bdf = bi.shortDiff; for (i = 0, cb = 0; cb < 13; ++cb) { const int l = (*bdf++) >> 1; for (int lwin = 0; lwin < 3; ++lwin) { *mp++ = l; *mp++ = i + lwin; *mp++ = lwin; *mp++ = cb; } i += 6 * l; } mapEnd[j][1] = mp; mp = map[j][2] = mapbuf2[j]; bdf = bi.longDiff; for (cb = 0; cb < 22; ++cb) { *mp++ = (*bdf++) >> 1; *mp++ = cb; } mapEnd[j][2] = mp; } for (j = 0; j < 9; ++j) { for (i = 0; i < 23; ++i) longLimit[j][i] = jmin (32, (bandInfo[j].longIndex[i] - 1 + 8) / 18 + 1); for (i = 0; i < 14; ++i) shortLimit[j][i] = jmin (32, (bandInfo[j].shortIndex[i] - 1) / 18 + 1); } for (i = 0; i < 5; ++i) for (j = 0; j < 6; ++j) for (int k = 0; k < 6; ++k) { const int n = k + j * 6 + i * 36; iLength2[n] = (unsigned int) (i | (j << 3) | (k << 6) | (3 << 12)); } for (i = 0; i < 4; ++i) for (j = 0; j < 4; ++j) for (int k = 0; k < 4; ++k) { const int n = k + j * 4 + i * 16; iLength2[n + 180] = (unsigned int) (i | (j << 3) | (k << 6) | (4 << 12)); } for (i = 0; i < 4; ++i) for (j = 0; j < 3; ++j) { const int n = j + i * 3; iLength2[n + 244] = (unsigned int) (i | (j << 3) | (5 << 12)); nLength2[n + 500] = (unsigned int) (i | (j << 3) | (2 << 12) | (1 << 15)); } for (i = 0; i < 5; ++i) for (j = 0; j < 5; ++j) for (int k = 0; k < 4; ++k) for (int l = 0; l < 4; ++l) { const int n = l + k * 4 + j * 16 + i * 80; nLength2[n] = (unsigned int) (i | (j << 3) | (k << 6) | (l << 9) | (0 << 12)); } for (i = 0; i < 5; ++i) for (j = 0; j < 5; ++j) for (int k = 0; k < 4; ++k) { const int n = k + j * 4 + i * 20; nLength2[n + 400] = (unsigned int) (i | (j << 3) | (k << 6) | (1 << 12)); } } }; static const Constants constants; //============================================================================== struct Layer3SideInfo { struct Info { void doAntialias (float xr[32][18]) const noexcept { float* xr1 = xr[1]; int sb; if (blockType == 2) { if (mixedBlockFlag == 0) return; sb = 1; } else sb = (int) maxb - 1; for (; sb != 0; --sb, xr1 += 10) { const float* cs = constants.antiAliasingCs; const float* ca = constants.antiAliasingCa; float* xr2 = xr1; for (int ss = 7; ss >= 0; --ss) { const float bu = *--xr2, bd = *xr1; *xr2 = (bu * *cs) - (bd * *ca); *xr1++ = (bd * *cs++) + (bu * *ca++); } } } void doIStereo (float xrBuffer[2][32][18], const int* const scaleFactors, const int sampleRate, const bool msStereo, const int lsf) const noexcept { float (*xr) [32 * 18] = (float (*) [32 * 18]) xrBuffer; const BandInfoStruct& bi = bandInfo[sampleRate]; const float* tabl1, *tabl2; if (lsf != 0) { const int p = scaleFactorCompression & 1; if (msStereo) { tabl1 = constants.pow1_2[p]; tabl2 = constants.pow2_2[p]; } else { tabl1 = constants.pow1_1[p]; tabl2 = constants.pow2_1[p]; } } else { if (msStereo) { tabl1 = constants.tan1_2; tabl2 = constants.tan2_2; } else { tabl1 = constants.tan1_1; tabl2 = constants.tan2_1; } } if (blockType == 2) { bool doL = mixedBlockFlag != 0; for (uint32 lwin = 0; lwin < 3; ++lwin) { uint32 sfb = maxBand[lwin]; doL = doL && (sfb <= 3); for (; sfb < 12; ++sfb) { const int p = scaleFactors[sfb * 3 + lwin - mixedBlockFlag]; if (p != 7) { const float t1 = tabl1[p]; const float t2 = tabl2[p]; int sb = bi.shortDiff[sfb]; uint32 index = (uint32) sb + lwin; for (; sb > 0; --sb, index += 3) { float v = xr[0][index]; xr[0][index] = v * t1; xr[1][index] = v * t2; } } } const int p = scaleFactors[11 * 3 + lwin - mixedBlockFlag]; if (p != 7) { const float t1 = tabl1[p]; const float t2 = tabl2[p]; int sb = bi.shortDiff[12]; uint32 index = (uint32) sb + lwin; for (; sb > 0; --sb, index += 3) { float v = xr[0][index]; xr[0][index] = v * t1; xr[1][index] = v * t2; } } } if (doL) { int index = bi.longIndex[maxBandl]; for (uint32 sfb = maxBandl; sfb < 8; ++sfb) { int sb = bi.longDiff[sfb]; const int p = scaleFactors[sfb]; if (p != 7) { const float t1 = tabl1[p]; const float t2 = tabl2[p]; for (; sb > 0; --sb, ++index) { float v = xr[0][index]; xr[0][index] = v * t1; xr[1][index] = v * t2; } } else index += sb; } } } else { int index = bi.longIndex[maxBandl]; for (uint32 sfb = maxBandl; sfb < 21; ++sfb) { int sb = bi.longDiff[sfb]; const int p = scaleFactors[sfb]; if (p != 7) { const float t1 = tabl1[p]; const float t2 = tabl2[p]; for (; sb > 0; --sb, ++index) { const float v = xr[0][index]; xr[0][index] = v * t1; xr[1][index] = v * t2; } } else index += sb; } const int p = scaleFactors[20]; if (p != 7) { const float t1 = tabl1[p], t2 = tabl2[p]; for (int sb = bi.longDiff[21]; sb > 0; --sb, ++index) { const float v = xr[0][index]; xr[0][index] = v * t1; xr[1][index] = v * t2; } } } } int scfsi; uint32 part2_3Length, bigValues; uint32 scaleFactorCompression, blockType, mixedBlockFlag; uint32 tableSelect[3]; uint32 maxBand[3]; uint32 maxBandl, maxb, region1Start, region2Start; uint32 preflag, scaleFactorScale, count1TableSelect; const float* fullGain[3]; const float* pow2gain; }; struct InfoPair { Info gr[2]; }; InfoPair ch[2]; uint32 mainDataStart, privateBits; }; //============================================================================== namespace DCT { enum { SBLIMIT = 32 }; static const float cos6_1 = 0.866025388f; static const float cos6_2 = 0.5f; static const float cos9[] = { 1.0f, 0.98480773f, 0.939692616f, 0.866025388f, 0.766044438f, 0.642787635f, 0.5f, 0.342020154f, 0.173648179f }; static const float cos36[] = { 0.501909912f, 0.517638087f, 0.551688969f, 0.610387266f, 0.707106769f, 0.871723413f, 1.18310082f, 1.93185163f, 5.73685646f }; static const float cos12[] = { 0.517638087f, 0.707106769f, 1.93185163f }; inline void dct36_0 (const int v, float* const ts, float* const out1, float* const out2, const float* const wintab, float sum0, const float sum1) noexcept { const float tmp = sum0 + sum1; out2[9 + v] = tmp * wintab[27 + v]; out2[8 - v] = tmp * wintab[26 - v]; sum0 -= sum1; ts[SBLIMIT * (8 - v)] = out1[8 - v] + sum0 * wintab[8 - v]; ts[SBLIMIT * (9 + v)] = out1[9 + v] + sum0 * wintab[9 + v]; } inline void dct36_1 (const int v, float* const ts, float* const out1, float* const out2, const float* const wintab, const float tmp1a, const float tmp1b, const float tmp2a, const float tmp2b) noexcept { dct36_0 (v, ts, out1, out2, wintab, tmp1a + tmp2a, (tmp1b + tmp2b) * cos36[v]); } inline void dct36_2 (const int v, float* const ts, float* const out1, float* const out2, const float* const wintab, const float tmp1a, const float tmp1b, const float tmp2a, const float tmp2b) noexcept { dct36_0 (v, ts, out1, out2, wintab, tmp2a - tmp1a, (tmp2b - tmp1b) * cos36[v]); } static void dct36 (float* const in, float* const out1, float* const out2, const float* const wintab, float* const ts) noexcept { in[17] += in[16]; in[16] += in[15]; in[15] += in[14]; in[14] += in[13]; in[13] += in[12]; in[12] += in[11]; in[11] += in[10]; in[10] += in[9]; in[9] += in[8]; in[8] += in[7]; in[7] += in[6]; in[6] += in[5]; in[5] += in[4]; in[4] += in[3]; in[3] += in[2]; in[2] += in[1]; in[1] += in[0]; in[17] += in[15]; in[15] += in[13]; in[13] += in[11]; in[11] += in[9]; in[9] += in[7]; in[7] += in[5]; in[5] += in[3]; in[3] += in[1]; const float ta33 = in[6] * cos9[3]; const float ta66 = in[12] * cos9[6]; const float tb33 = in[7] * cos9[3]; const float tb66 = in[13] * cos9[6]; { const float tmp1a = in[2] * cos9[1] + ta33 + in[10] * cos9[5] + in[14] * cos9[7]; const float tmp1b = in[3] * cos9[1] + tb33 + in[11] * cos9[5] + in[15] * cos9[7]; const float tmp2a = in[0] + in[4] * cos9[2] + in[8] * cos9[4] + ta66 + in[16] * cos9[8]; const float tmp2b = in[1] + in[5] * cos9[2] + in[9] * cos9[4] + tb66 + in[17] * cos9[8]; dct36_1 (0, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); dct36_2 (8, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); } { const float tmp1a = (in[2] - in[10] - in[14]) * cos9[3]; const float tmp1b = (in[3] - in[11] - in[15]) * cos9[3]; const float tmp2a = (in[4] - in[8] - in[16]) * cos9[6] - in[12] + in[0]; const float tmp2b = (in[5] - in[9] - in[17]) * cos9[6] - in[13] + in[1]; dct36_1 (1, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); dct36_2 (7, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); } { const float tmp1a = in[2] * cos9[5] - ta33 - in[10] * cos9[7] + in[14] * cos9[1]; const float tmp1b = in[3] * cos9[5] - tb33 - in[11] * cos9[7] + in[15] * cos9[1]; const float tmp2a = in[0] - in[4] * cos9[8] - in[8] * cos9[2] + ta66 + in[16] * cos9[4]; const float tmp2b = in[1] - in[5] * cos9[8] - in[9] * cos9[2] + tb66 + in[17] * cos9[4]; dct36_1 (2, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); dct36_2 (6, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); } { const float tmp1a = in[2] * cos9[7] - ta33 + in[10] * cos9[1] - in[14] * cos9[5]; const float tmp1b = in[3] * cos9[7] - tb33 + in[11] * cos9[1] - in[15] * cos9[5]; const float tmp2a = in[0] - in[4] * cos9[4] + in[8] * cos9[8] + ta66 - in[16] * cos9[2]; const float tmp2b = in[1] - in[5] * cos9[4] + in[9] * cos9[8] + tb66 - in[17] * cos9[2]; dct36_1 (3, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); dct36_2 (5, ts, out1, out2, wintab, tmp1a, tmp1b, tmp2a, tmp2b); } const float sum0 = in[0] - in[4] + in[8] - in[12] + in[16]; const float sum1 = (in[1] - in[5] + in[9] - in[13] + in[17]) * cos36[4]; dct36_0 (4, ts, out1, out2, wintab, sum0, sum1); } struct DCT12Inputs { float in0, in1, in2, in3, in4, in5; inline DCT12Inputs (const float* const in) noexcept { in5 = in[5*3] + (in4 = in[4*3]); in4 += (in3 = in[3*3]); in3 += (in2 = in[2*3]); in2 += (in1 = in[1*3]); in1 += (in0 = in[0*3]); in5 += in3; in3 += in1; in2 *= cos6_1; in3 *= cos6_1; } inline void process() noexcept { in0 += in4 * cos6_2; in4 = in0 + in2; in0 -= in2; in1 += in5 * cos6_2; in5 = (in1 + in3) * cos12[0]; in1 = (in1 - in3) * cos12[2]; in3 = in4 + in5; in4 -= in5; in2 = in0 + in1; in0 -= in1; } }; static void dct12 (const float* in, float* const out1, float* const out2, const float* wi, float* ts) noexcept { { ts[0] = out1[0]; ts[SBLIMIT * 1] = out1[1]; ts[SBLIMIT * 2] = out1[2]; ts[SBLIMIT * 3] = out1[3]; ts[SBLIMIT * 4] = out1[4]; ts[SBLIMIT * 5] = out1[5]; DCT12Inputs inputs (in); { float tmp1 = (inputs.in0 - inputs.in4); const float tmp2 = (inputs.in1 - inputs.in5) * cos12[1]; const float tmp0 = tmp1 + tmp2; tmp1 -= tmp2; ts[16 * SBLIMIT] = out1[16] + tmp0 * wi[10]; ts[13 * SBLIMIT] = out1[13] + tmp0 * wi[7]; ts[7 * SBLIMIT] = out1[7] + tmp1 * wi[1]; ts[10 * SBLIMIT] = out1[10] + tmp1 * wi[4]; } inputs.process(); ts[17 * SBLIMIT] = out1[17] + inputs.in2 * wi[11]; ts[12 * SBLIMIT] = out1[12] + inputs.in2 * wi[6]; ts[14 * SBLIMIT] = out1[14] + inputs.in3 * wi[8]; ts[15 * SBLIMIT] = out1[15] + inputs.in3 * wi[9]; ts[6 * SBLIMIT] = out1[6] + inputs.in0 * wi[0]; ts[11 * SBLIMIT] = out1[11] + inputs.in0 * wi[5]; ts[8 * SBLIMIT] = out1[8] + inputs.in4 * wi[2]; ts[9 * SBLIMIT] = out1[9] + inputs.in4 * wi[3]; } { DCT12Inputs inputs (++in); float tmp1 = (inputs.in0 - inputs.in4); const float tmp2 = (inputs.in1 - inputs.in5) * cos12[1]; const float tmp0 = tmp1 + tmp2; tmp1 -= tmp2; out2[4] = tmp0 * wi[10]; out2[1] = tmp0 * wi[7]; ts[13 * SBLIMIT] += tmp1 * wi[1]; ts[16 * SBLIMIT] += tmp1 * wi[4]; inputs.process(); out2[5] = inputs.in2 * wi[11]; out2[0] = inputs.in2 * wi[6]; out2[2] = inputs.in3 * wi[8]; out2[3] = inputs.in3 * wi[9]; ts[12 * SBLIMIT] += inputs.in0 * wi[0]; ts[17 * SBLIMIT] += inputs.in0 * wi[5]; ts[14 * SBLIMIT] += inputs.in4 * wi[2]; ts[15 * SBLIMIT] += inputs.in4 * wi[5 - 2]; } { DCT12Inputs inputs (++in); out2[12] = out2[13] = out2[14] = out2[15] = out2[16] = out2[17] = 0; float tmp1 = (inputs.in0 - inputs.in4); const float tmp2 = (inputs.in1 - inputs.in5) * cos12[1]; const float tmp0 = tmp1 + tmp2; tmp1 -= tmp2; out2[10] = tmp0 * wi[10]; out2[7] = tmp0 * wi[7]; out2[1] += tmp1 * wi[1]; out2[4] += tmp1 * wi[4]; inputs.process(); out2[11] = inputs.in2 * wi[11]; out2[6] = inputs.in2 * wi[6]; out2[8] = inputs.in3 * wi[8]; out2[9] = inputs.in3 * wi[9]; out2[0] += inputs.in0 * wi[0]; out2[5] += inputs.in0 * wi[5]; out2[2] += inputs.in4 * wi[2]; out2[3] += inputs.in4 * wi[3]; } } static void dct64 (float* const out0, float* const out1, float* const b1, float* const b2, const float* const samples) noexcept { { const float* const costab = constants.cosTables[0]; b1[0x00] = samples[0x00] + samples[0x1F]; b1[0x1F] = (samples[0x00] - samples[0x1F]) * costab[0x0]; b1[0x01] = samples[0x01] + samples[0x1E]; b1[0x1E] = (samples[0x01] - samples[0x1E]) * costab[0x1]; b1[0x02] = samples[0x02] + samples[0x1D]; b1[0x1D] = (samples[0x02] - samples[0x1D]) * costab[0x2]; b1[0x03] = samples[0x03] + samples[0x1C]; b1[0x1C] = (samples[0x03] - samples[0x1C]) * costab[0x3]; b1[0x04] = samples[0x04] + samples[0x1B]; b1[0x1B] = (samples[0x04] - samples[0x1B]) * costab[0x4]; b1[0x05] = samples[0x05] + samples[0x1A]; b1[0x1A] = (samples[0x05] - samples[0x1A]) * costab[0x5]; b1[0x06] = samples[0x06] + samples[0x19]; b1[0x19] = (samples[0x06] - samples[0x19]) * costab[0x6]; b1[0x07] = samples[0x07] + samples[0x18]; b1[0x18] = (samples[0x07] - samples[0x18]) * costab[0x7]; b1[0x08] = samples[0x08] + samples[0x17]; b1[0x17] = (samples[0x08] - samples[0x17]) * costab[0x8]; b1[0x09] = samples[0x09] + samples[0x16]; b1[0x16] = (samples[0x09] - samples[0x16]) * costab[0x9]; b1[0x0A] = samples[0x0A] + samples[0x15]; b1[0x15] = (samples[0x0A] - samples[0x15]) * costab[0xA]; b1[0x0B] = samples[0x0B] + samples[0x14]; b1[0x14] = (samples[0x0B] - samples[0x14]) * costab[0xB]; b1[0x0C] = samples[0x0C] + samples[0x13]; b1[0x13] = (samples[0x0C] - samples[0x13]) * costab[0xC]; b1[0x0D] = samples[0x0D] + samples[0x12]; b1[0x12] = (samples[0x0D] - samples[0x12]) * costab[0xD]; b1[0x0E] = samples[0x0E] + samples[0x11]; b1[0x11] = (samples[0x0E] - samples[0x11]) * costab[0xE]; b1[0x0F] = samples[0x0F] + samples[0x10]; b1[0x10] = (samples[0x0F] - samples[0x10]) * costab[0xF]; } { const float* const costab = constants.cosTables[1]; b2[0x00] = b1[0x00] + b1[0x0F]; b2[0x0F] = (b1[0x00] - b1[0x0F]) * costab[0]; b2[0x01] = b1[0x01] + b1[0x0E]; b2[0x0E] = (b1[0x01] - b1[0x0E]) * costab[1]; b2[0x02] = b1[0x02] + b1[0x0D]; b2[0x0D] = (b1[0x02] - b1[0x0D]) * costab[2]; b2[0x03] = b1[0x03] + b1[0x0C]; b2[0x0C] = (b1[0x03] - b1[0x0C]) * costab[3]; b2[0x04] = b1[0x04] + b1[0x0B]; b2[0x0B] = (b1[0x04] - b1[0x0B]) * costab[4]; b2[0x05] = b1[0x05] + b1[0x0A]; b2[0x0A] = (b1[0x05] - b1[0x0A]) * costab[5]; b2[0x06] = b1[0x06] + b1[0x09]; b2[0x09] = (b1[0x06] - b1[0x09]) * costab[6]; b2[0x07] = b1[0x07] + b1[0x08]; b2[0x08] = (b1[0x07] - b1[0x08]) * costab[7]; b2[0x10] = b1[0x10] + b1[0x1F]; b2[0x1F] = (b1[0x1F] - b1[0x10]) * costab[0]; b2[0x11] = b1[0x11] + b1[0x1E]; b2[0x1E] = (b1[0x1E] - b1[0x11]) * costab[1]; b2[0x12] = b1[0x12] + b1[0x1D]; b2[0x1D] = (b1[0x1D] - b1[0x12]) * costab[2]; b2[0x13] = b1[0x13] + b1[0x1C]; b2[0x1C] = (b1[0x1C] - b1[0x13]) * costab[3]; b2[0x14] = b1[0x14] + b1[0x1B]; b2[0x1B] = (b1[0x1B] - b1[0x14]) * costab[4]; b2[0x15] = b1[0x15] + b1[0x1A]; b2[0x1A] = (b1[0x1A] - b1[0x15]) * costab[5]; b2[0x16] = b1[0x16] + b1[0x19]; b2[0x19] = (b1[0x19] - b1[0x16]) * costab[6]; b2[0x17] = b1[0x17] + b1[0x18]; b2[0x18] = (b1[0x18] - b1[0x17]) * costab[7]; } { const float* const costab = constants.cosTables[2]; b1[0x00] = b2[0x00] + b2[0x07]; b1[0x07] = (b2[0x00] - b2[0x07]) * costab[0]; b1[0x01] = b2[0x01] + b2[0x06]; b1[0x06] = (b2[0x01] - b2[0x06]) * costab[1]; b1[0x02] = b2[0x02] + b2[0x05]; b1[0x05] = (b2[0x02] - b2[0x05]) * costab[2]; b1[0x03] = b2[0x03] + b2[0x04]; b1[0x04] = (b2[0x03] - b2[0x04]) * costab[3]; b1[0x08] = b2[0x08] + b2[0x0F]; b1[0x0F] = (b2[0x0F] - b2[0x08]) * costab[0]; b1[0x09] = b2[0x09] + b2[0x0E]; b1[0x0E] = (b2[0x0E] - b2[0x09]) * costab[1]; b1[0x0A] = b2[0x0A] + b2[0x0D]; b1[0x0D] = (b2[0x0D] - b2[0x0A]) * costab[2]; b1[0x0B] = b2[0x0B] + b2[0x0C]; b1[0x0C] = (b2[0x0C] - b2[0x0B]) * costab[3]; b1[0x10] = b2[0x10] + b2[0x17]; b1[0x17] = (b2[0x10] - b2[0x17]) * costab[0]; b1[0x11] = b2[0x11] + b2[0x16]; b1[0x16] = (b2[0x11] - b2[0x16]) * costab[1]; b1[0x12] = b2[0x12] + b2[0x15]; b1[0x15] = (b2[0x12] - b2[0x15]) * costab[2]; b1[0x13] = b2[0x13] + b2[0x14]; b1[0x14] = (b2[0x13] - b2[0x14]) * costab[3]; b1[0x18] = b2[0x18] + b2[0x1F]; b1[0x1F] = (b2[0x1F] - b2[0x18]) * costab[0]; b1[0x19] = b2[0x19] + b2[0x1E]; b1[0x1E] = (b2[0x1E] - b2[0x19]) * costab[1]; b1[0x1A] = b2[0x1A] + b2[0x1D]; b1[0x1D] = (b2[0x1D] - b2[0x1A]) * costab[2]; b1[0x1B] = b2[0x1B] + b2[0x1C]; b1[0x1C] = (b2[0x1C] - b2[0x1B]) * costab[3]; } { const float cos0 = constants.cosTables[3][0]; const float cos1 = constants.cosTables[3][1]; b2[0x00] = b1[0x00] + b1[0x03]; b2[0x03] = (b1[0x00] - b1[0x03]) * cos0; b2[0x01] = b1[0x01] + b1[0x02]; b2[0x02] = (b1[0x01] - b1[0x02]) * cos1; b2[0x04] = b1[0x04] + b1[0x07]; b2[0x07] = (b1[0x07] - b1[0x04]) * cos0; b2[0x05] = b1[0x05] + b1[0x06]; b2[0x06] = (b1[0x06] - b1[0x05]) * cos1; b2[0x08] = b1[0x08] + b1[0x0B]; b2[0x0B] = (b1[0x08] - b1[0x0B]) * cos0; b2[0x09] = b1[0x09] + b1[0x0A]; b2[0x0A] = (b1[0x09] - b1[0x0A]) * cos1; b2[0x0C] = b1[0x0C] + b1[0x0F]; b2[0x0F] = (b1[0x0F] - b1[0x0C]) * cos0; b2[0x0D] = b1[0x0D] + b1[0x0E]; b2[0x0E] = (b1[0x0E] - b1[0x0D]) * cos1; b2[0x10] = b1[0x10] + b1[0x13]; b2[0x13] = (b1[0x10] - b1[0x13]) * cos0; b2[0x11] = b1[0x11] + b1[0x12]; b2[0x12] = (b1[0x11] - b1[0x12]) * cos1; b2[0x14] = b1[0x14] + b1[0x17]; b2[0x17] = (b1[0x17] - b1[0x14]) * cos0; b2[0x15] = b1[0x15] + b1[0x16]; b2[0x16] = (b1[0x16] - b1[0x15]) * cos1; b2[0x18] = b1[0x18] + b1[0x1B]; b2[0x1B] = (b1[0x18] - b1[0x1B]) * cos0; b2[0x19] = b1[0x19] + b1[0x1A]; b2[0x1A] = (b1[0x19] - b1[0x1A]) * cos1; b2[0x1C] = b1[0x1C] + b1[0x1F]; b2[0x1F] = (b1[0x1F] - b1[0x1C]) * cos0; b2[0x1D] = b1[0x1D] + b1[0x1E]; b2[0x1E] = (b1[0x1E] - b1[0x1D]) * cos1; } { const float cos0 = constants.cosTables[4][0]; b1[0x00] = b2[0x00] + b2[0x01]; b1[0x01] = (b2[0x00] - b2[0x01]) * cos0; b1[0x02] = b2[0x02] + b2[0x03]; b1[0x03] = (b2[0x03] - b2[0x02]) * cos0; b1[0x02] += b1[0x03]; b1[0x04] = b2[0x04] + b2[0x05]; b1[0x05] = (b2[0x04] - b2[0x05]) * cos0; b1[0x06] = b2[0x06] + b2[0x07]; b1[0x07] = (b2[0x07] - b2[0x06]) * cos0; b1[0x06] += b1[0x07]; b1[0x04] += b1[0x06]; b1[0x06] += b1[0x05]; b1[0x05] += b1[0x07]; b1[0x08] = b2[0x08] + b2[0x09]; b1[0x09] = (b2[0x08] - b2[0x09]) * cos0; b1[0x0A] = b2[0x0A] + b2[0x0B]; b1[0x0B] = (b2[0x0B] - b2[0x0A]) * cos0; b1[0x0A] += b1[0x0B]; b1[0x0C] = b2[0x0C] + b2[0x0D]; b1[0x0D] = (b2[0x0C] - b2[0x0D]) * cos0; b1[0x0E] = b2[0x0E] + b2[0x0F]; b1[0x0F] = (b2[0x0F] - b2[0x0E]) * cos0; b1[0x0E] += b1[0x0F]; b1[0x0C] += b1[0x0E]; b1[0x0E] += b1[0x0D]; b1[0x0D] += b1[0x0F]; b1[0x10] = b2[0x10] + b2[0x11]; b1[0x11] = (b2[0x10] - b2[0x11]) * cos0; b1[0x12] = b2[0x12] + b2[0x13]; b1[0x13] = (b2[0x13] - b2[0x12]) * cos0; b1[0x12] += b1[0x13]; b1[0x14] = b2[0x14] + b2[0x15]; b1[0x15] = (b2[0x14] - b2[0x15]) * cos0; b1[0x16] = b2[0x16] + b2[0x17]; b1[0x17] = (b2[0x17] - b2[0x16]) * cos0; b1[0x16] += b1[0x17]; b1[0x14] += b1[0x16]; b1[0x16] += b1[0x15]; b1[0x15] += b1[0x17]; b1[0x18] = b2[0x18] + b2[0x19]; b1[0x19] = (b2[0x18] - b2[0x19]) * cos0; b1[0x1A] = b2[0x1A] + b2[0x1B]; b1[0x1B] = (b2[0x1B] - b2[0x1A]) * cos0; b1[0x1A] += b1[0x1B]; b1[0x1C] = b2[0x1C] + b2[0x1D]; b1[0x1D] = (b2[0x1C] - b2[0x1D]) * cos0; b1[0x1E] = b2[0x1E] + b2[0x1F]; b1[0x1F] = (b2[0x1F] - b2[0x1E]) * cos0; b1[0x1E] += b1[0x1F]; b1[0x1C] += b1[0x1E]; b1[0x1E] += b1[0x1D]; b1[0x1D] += b1[0x1F]; } out0[0x10 * 16] = b1[0x00]; out0[0x10 * 12] = b1[0x04]; out0[0x10 * 8] = b1[0x02]; out0[0x10 * 4] = b1[0x06]; out0[0] = b1[0x01]; out1[0] = b1[0x01]; out1[0x10 * 4] = b1[0x05]; out1[0x10 * 8] = b1[0x03]; out1[0x10 * 12] = b1[0x07]; b1[0x08] += b1[0x0C]; out0[0x10 * 14] = b1[0x08]; b1[0x0C] += b1[0x0a]; out0[0x10 * 10] = b1[0x0C]; b1[0x0A] += b1[0x0E]; out0[0x10 * 6] = b1[0x0A]; b1[0x0E] += b1[0x09]; out0[0x10 * 2] = b1[0x0E]; b1[0x09] += b1[0x0D]; out1[0x10 * 2] = b1[0x09]; b1[0x0D] += b1[0x0B]; out1[0x10 * 6] = b1[0x0D]; b1[0x0B] += b1[0x0F]; out1[0x10 * 10] = b1[0x0B]; out1[0x10 * 14] = b1[0x0F]; b1[0x18] += b1[0x1C]; out0[0x10 * 15] = b1[0x10] + b1[0x18]; out0[0x10 * 13] = b1[0x18] + b1[0x14]; b1[0x1C] += b1[0x1a]; out0[0x10 * 11] = b1[0x14] + b1[0x1C]; out0[0x10 * 9] = b1[0x1C] + b1[0x12]; b1[0x1A] += b1[0x1E]; out0[0x10 * 7] = b1[0x12] + b1[0x1A]; out0[0x10 * 5] = b1[0x1A] + b1[0x16]; b1[0x1E] += b1[0x19]; out0[0x10 * 3] = b1[0x16] + b1[0x1E]; out0[0x10 * 1] = b1[0x1E] + b1[0x11]; b1[0x19] += b1[0x1D]; out1[0x10 * 1] = b1[0x11] + b1[0x19]; out1[0x10 * 3] = b1[0x19] + b1[0x15]; b1[0x1D] += b1[0x1B]; out1[0x10 * 5] = b1[0x15] + b1[0x1D]; out1[0x10 * 7] = b1[0x1D] + b1[0x13]; b1[0x1B] += b1[0x1F]; out1[0x10 * 9] = b1[0x13] + b1[0x1B]; out1[0x10 * 11] = b1[0x1B] + b1[0x17]; out1[0x10 * 13] = b1[0x17] + b1[0x1F]; out1[0x10 * 15] = b1[0x1F]; } static void dct64 (float* const a, float* const b, const float* const c) noexcept { float temp[64]; dct64 (a, b, temp, temp + 32, c); } } //============================================================================== struct MP3Stream { MP3Stream (InputStream& source) : stream (source, 8192), numFrames (0), currentFrameIndex (0), vbrHeaderFound (false) { reset(); } int decodeNextBlock (float* const out0, float* const out1, int& done) { if (! headerParsed) { int nextFrameOffset = scanForNextFrameHeader (false); if (lastFrameSize == -1 || needToSyncBitStream) { needToSyncBitStream = false; readVBRHeader(); if (vbrHeaderFound) return 1; } if (nextFrameOffset < 0) return -1; if (nextFrameOffset > 0) { int size; wasFreeFormat = false; needToSyncBitStream = true; size = (int) (bufferPointer - (bufferSpace[bufferSpaceIndex] + 512)); if (size > 2880) { size = 0; bufferPointer = bufferSpace[bufferSpaceIndex] + 512; } const int toSkip = (size + nextFrameOffset) - 2880; if (toSkip > 0) { stream.skipNextBytes (toSkip); nextFrameOffset -= toSkip; } stream.read (bufferPointer, nextFrameOffset); lastFrameSize += nextFrameOffset; } frame.decodeHeader ((uint32) stream.readIntBigEndian()); headerParsed = true; frameSize = frame.frameSize; isFreeFormat = (frameSize == 0); sideInfoSize = frame.lsf != 0 ? ((frame.numChannels == 1) ? 9 : 17) : ((frame.numChannels == 1) ? 17 : 32); if (frame.crc16FollowsHeader) sideInfoSize += 2; bufferSpaceIndex = 1 - bufferSpaceIndex; bufferPointer = bufferSpace[bufferSpaceIndex] + 512; bitIndex = 0; if (lastFrameSize < 0) return 1; } if (! sideParsed) { if (frame.layer == 3) { stream.read (bufferPointer, sideInfoSize); if (frame.crc16FollowsHeader) getBits (16); const int bits = jmax (0, decodeLayer3SideInfo()); dataSize = (bits + 7) / 8; if (! isFreeFormat) dataSize = jmin (dataSize, frame.frameSize - sideInfoSize); } else { dataSize = frame.frameSize; sideInfoSize = 0; } sideParsed = true; } int result = 1; if (! dataParsed) { stream.read (bufferPointer, dataSize); if (out0 != nullptr) { if (frame.layer < 3 && frame.crc16FollowsHeader) getBits (16); switch (frame.layer) { case 1: decodeLayer1Frame (out0, out1, done); break; case 2: decodeLayer2Frame (out0, out1, done); break; case 3: decodeLayer3Frame (out0, out1, done); break; default: break; } } bufferPointer = bufferSpace[bufferSpaceIndex] + 512 + sideInfoSize + dataSize; dataParsed = true; result = 0; } if (isFreeFormat) { if (wasFreeFormat) { frameSize = lastFrameSizeNoPadding + frame.padding; } else { const int nextFrameOffset = scanForNextFrameHeader (true); wasFreeFormat = isFreeFormat; if (nextFrameOffset < 0) { lastFrameSize = frameSize; return result; } frameSize = nextFrameOffset + sideInfoSize + dataSize; lastFrameSizeNoPadding = frameSize - frame.padding; } } if (result == 0) return result; int bytes = frameSize - (sideInfoSize + dataSize); if (bytes > 0) { const int toSkip = bytes - 512; if (toSkip > 0) { stream.skipNextBytes (toSkip); bytes -= toSkip; frameSize -= toSkip; } stream.read (bufferPointer, bytes); bufferPointer += bytes; } lastFrameSize = frameSize; wasFreeFormat = isFreeFormat; frameSize = 0; headerParsed = sideParsed = dataParsed = false; return result; } bool seek (int frameIndex) { frameIndex = jmax (0, frameIndex); while (frameIndex >= frameStreamPositions.size() * storedStartPosInterval) { int dummy = 0; const int result = decodeNextBlock (nullptr, nullptr, dummy); if (result < 0) return false; if (result > 0) break; } frameIndex = jmin (frameIndex & ~(storedStartPosInterval - 1), frameStreamPositions.size() * storedStartPosInterval - 1); stream.setPosition (frameStreamPositions.getUnchecked (frameIndex / storedStartPosInterval)); currentFrameIndex = frameIndex; reset(); return true; } MP3Frame frame; VBRTagData vbrTagData; BufferedInputStream stream; int numFrames, currentFrameIndex; bool vbrHeaderFound; private: bool headerParsed, sideParsed, dataParsed, needToSyncBitStream; bool isFreeFormat, wasFreeFormat; int sideInfoSize, dataSize; int frameSize, lastFrameSize, lastFrameSizeNoPadding; int bufferSpaceIndex; Layer3SideInfo sideinfo; uint8 bufferSpace[2][2880 + 1024]; uint8* bufferPointer; int bitIndex, synthBo; float hybridBlock[2][2][32 * 18]; int hybridBlockIndex[2]; float synthBuffers[2][2][0x110]; float hybridIn[2][32][18]; float hybridOut[2][18][32]; void reset() noexcept { headerParsed = sideParsed = dataParsed = isFreeFormat = wasFreeFormat = false; lastFrameSize = -1; needToSyncBitStream = true; frameSize = sideInfoSize = dataSize = frameSize = bitIndex = 0; lastFrameSizeNoPadding = bufferSpaceIndex = 0; bufferPointer = bufferSpace[bufferSpaceIndex] + 512; synthBo = 1; zerostruct (sideinfo); zeromem (bufferSpace, sizeof (bufferSpace)); zeromem (hybridBlock, sizeof (hybridBlock)); zeromem (hybridBlockIndex, sizeof (hybridBlockIndex)); zeromem (synthBuffers, sizeof (synthBuffers)); } enum { storedStartPosInterval = 4 }; Array frameStreamPositions; struct SideInfoLayer1 { uint8 allocation[32][2]; uint8 scaleFactor[32][2]; }; struct SideInfoLayer2 { uint8 allocation[32][2]; uint8 scaleFactor[32][2][3]; }; static bool isValidHeader (const uint32 header, const int oldLayer) noexcept { const int newLayer = 4 - ((header >> 17) & 3); return (header & 0xffe00000) == 0xffe00000 && newLayer != 4 && (oldLayer <= 0 || newLayer == oldLayer) && ((header >> 12) & 15) != 15 && ((header >> 10) & 3) != 3 && (header & 3) != 2; } bool rollBackBufferPointer (int backstep) noexcept { if (lastFrameSize < 0 && backstep > 0) return false; const uint8* oldBuffer = bufferSpace[1 - bufferSpaceIndex] + 512; bufferPointer -= backstep; if (backstep != 0) memcpy (bufferPointer, oldBuffer + lastFrameSize - backstep, (size_t) backstep); bitIndex = 0; return true; } uint32 getBits (const int numBits) noexcept { if (numBits <= 0 || bufferPointer == nullptr) return 0; const uint32 result = ((((((bufferPointer[0] << 8) | bufferPointer[1]) << 8) | bufferPointer[2]) << bitIndex) & 0xffffff) >> (24 - numBits); bitIndex += numBits; bufferPointer += (bitIndex >> 3); bitIndex &= 7; return result; } uint32 getOneBit() noexcept { const uint8 result = (uint8) (*bufferPointer << bitIndex); ++bitIndex; bufferPointer += (bitIndex >> 3); bitIndex &= 7; return result >> 7; } uint32 getBitsUnchecked (const int numBits) noexcept { const uint32 result = ((((bufferPointer[0] << 8) | bufferPointer[1]) << bitIndex) & 0xffff) >> (16 - numBits); bitIndex += numBits; bufferPointer += (bitIndex >> 3); bitIndex &= 7; return result; } inline uint8 getBitsUint8 (const int numBits) noexcept { return (uint8) getBitsUnchecked (numBits); } inline uint16 getBitsUint16 (const int numBits) noexcept { return (uint16) getBitsUnchecked (numBits); } int scanForNextFrameHeader (const bool checkTypeAgainstLastFrame) noexcept { const int64 oldPos = stream.getPosition(); int offset = -3; uint32 header = 0; for (;;) { if (stream.isExhausted() || stream.getPosition() > oldPos + 32768) { offset = -1; break; } header = (header << 8) | (uint8) stream.readByte(); if (offset >= 0 && isValidHeader (header, frame.layer)) { if (! checkTypeAgainstLastFrame) break; const bool mpeg25 = (header & (1 << 20)) == 0; const uint32 lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); const uint32 sampleRateIndex = mpeg25 ? (6 + ((header >> 10) & 3)) : (((header >> 10) & 3) + (lsf * 3)); const uint32 mode = (header >> 6) & 3; const uint32 numChannels = (mode == 3) ? 1 : 2; if (numChannels == (uint32) frame.numChannels && lsf == (uint32) frame.lsf && mpeg25 == frame.mpeg25 && sampleRateIndex == (uint32) frame.sampleRateIndex) break; } ++offset; } if (offset >= 0) { if ((currentFrameIndex & (storedStartPosInterval - 1)) == 0) frameStreamPositions.set (currentFrameIndex / storedStartPosInterval, oldPos + offset); ++currentFrameIndex; } stream.setPosition (oldPos); return offset; } void readVBRHeader() { int64 oldPos = stream.getPosition(); uint8 xing[194]; stream.read (xing, sizeof (xing)); vbrHeaderFound = vbrTagData.read (xing); if (vbrHeaderFound) { numFrames = (int) vbrTagData.frames; oldPos += jmax (vbrTagData.headersize, 1); } stream.setPosition (oldPos); } void decodeLayer1Frame (float* const pcm0, float* const pcm1, int& samplesDone) noexcept { float fraction[2][32]; SideInfoLayer1 si; layer1Step1 (si); const int single = (frame.numChannels == 1 || frame.single == 3) ? 0 : frame.single; if (single >= 0) { for (int i = 0; i < 12; ++i) { layer1Step2 (si, fraction); synthesise (fraction[single], 0, pcm0, samplesDone); } } else { for (int i = 0; i < 12; ++i) { layer1Step2 (si, fraction); synthesiseStereo (fraction[0], fraction[1], pcm0, pcm1, samplesDone); } } } void decodeLayer2Frame (float* const pcm0, float* const pcm1, int& samplesDone) { float fraction[2][4][32]; frame.selectLayer2Table(); SideInfoLayer2 si; layer2Step1 (si); const int single = (frame.numChannels == 1 || frame.single == 3) ? 0 : frame.single; if (single >= 0) { for (int i = 0; i < 12; ++i) { layer2Step2 (si, i >> 2, fraction); for (int j = 0; j < 3; ++j) synthesise (fraction[single][j], 0, pcm0, samplesDone); } } else { for (int i = 0; i < 12; ++i) { layer2Step2 (si, i >> 2, fraction); for (int j = 0; j < 3; ++j) synthesiseStereo (fraction[0][j], fraction[1][j], pcm0, pcm1, samplesDone); } } } void decodeLayer3Frame (float* const pcm0, float* const pcm1, int& samplesDone) noexcept { if (! rollBackBufferPointer ((int) sideinfo.mainDataStart)) return; const int single = frame.numChannels == 1 ? 0 : frame.single; const int numChans = (frame.numChannels == 1 || single >= 0) ? 1 : 2; const bool msStereo = (frame.mode == 1) && (frame.modeExt & 2) != 0; const bool iStereo = (frame.mode == 1) && (frame.modeExt & 1) != 0; const int granules = frame.lsf ? 1 : 2; int scaleFactors[2][39]; for (int gr = 0; gr < granules; ++gr) { { Layer3SideInfo::Info& granule = sideinfo.ch[0].gr[gr]; const int part2bits = frame.lsf ? getLayer3ScaleFactors2 (scaleFactors[0], granule, 0) : getLayer3ScaleFactors1 (scaleFactors[0], granule); if (layer3DequantizeSample (hybridIn[0], scaleFactors[0], granule, frame.sampleRateIndex, part2bits)) return; } if (frame.numChannels == 2) { Layer3SideInfo::Info& granule = sideinfo.ch[1].gr[gr]; const int part2bits = frame.lsf ? getLayer3ScaleFactors2 (scaleFactors[1], granule, iStereo) : getLayer3ScaleFactors1 (scaleFactors[1], granule); if (layer3DequantizeSample (hybridIn[1], scaleFactors[1], granule, frame.sampleRateIndex, part2bits)) return; if (msStereo) { for (int i = 0; i < 32 * 18; ++i) { const float tmp0 = ((const float*) hybridIn[0]) [i]; const float tmp1 = ((const float*) hybridIn[1]) [i]; ((float*) hybridIn[1]) [i] = tmp0 - tmp1; ((float*) hybridIn[0]) [i] = tmp0 + tmp1; } } if (iStereo) granule.doIStereo (hybridIn, scaleFactors[1], frame.sampleRateIndex, msStereo, frame.lsf); if (msStereo || iStereo || single == 3) { if (granule.maxb > sideinfo.ch[0].gr[gr].maxb) sideinfo.ch[0].gr[gr].maxb = granule.maxb; else granule.maxb = sideinfo.ch[0].gr[gr].maxb; } switch (single) { case 3: { float* in0 = (float*) hybridIn[0]; const float* in1 = (const float*) hybridIn[1]; for (int i = 0; i < (int) (18 * granule.maxb); ++i, ++in0) *in0 = (*in0 + *in1++); } break; case 1: { float* in0 = (float*) hybridIn[0]; const float* in1 = (const float*) hybridIn[1]; for (int i = 0; i < (int) (18 * granule.maxb); ++i) *in0++ = *in1++; } break; } } for (int ch = 0; ch < numChans; ++ch) { const Layer3SideInfo::Info& granule = sideinfo.ch[ch].gr[gr]; granule.doAntialias (hybridIn[ch]); layer3Hybrid (hybridIn[ch], hybridOut[ch], ch, granule); } for (int ss = 0; ss < 18; ++ss) { if (single >= 0) synthesise (hybridOut[0][ss], 0, pcm0, samplesDone); else synthesiseStereo (hybridOut[0][ss], hybridOut[1][ss], pcm0, pcm1, samplesDone); } } } int decodeLayer3SideInfo() noexcept { const int numChannels = frame.numChannels; const int sampleRate = frame.sampleRateIndex; const int single = (numChannels == 1) ? 0 : frame.single; const bool msStereo = (frame.mode == 1) && (frame.modeExt & 2) != 0; const int granules = frame.lsf ? 1 : 2; if (frame.lsf == 0) getLayer3SideInfo1 (numChannels, msStereo, sampleRate, single); else getLayer3SideInfo2 (numChannels, msStereo, sampleRate, single); int databits = 0; for (int gr = 0; gr < granules; ++gr) for (int ch = 0; ch < numChannels; ++ch) databits += sideinfo.ch[ch].gr[gr].part2_3Length; return databits - 8 * (int) sideinfo.mainDataStart; } void layer1Step1 (SideInfoLayer1& si) noexcept { zerostruct (si); int i, jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : 32; if (frame.numChannels == 2) { for (i = 0; i < jsbound; ++i) { si.allocation[i][0] = getBitsUint8 (4); si.allocation[i][1] = getBitsUint8 (4); } for (i = jsbound; i < 32; ++i) si.allocation[i][0] = si.allocation[i][1] = getBitsUint8 (4); for (i = 0; i < 32; ++i) { si.scaleFactor[i][0] = si.allocation[i][0] ? getBitsUint8 (6) : 0; si.scaleFactor[i][1] = si.allocation[i][1] ? getBitsUint8 (6) : 0; } } else { for (i = 0; i < 32; ++i) si.allocation[i][0] = getBitsUint8 (4); for (i = 0; i < 32; ++i) si.scaleFactor[i][0] = si.allocation[i][0] ? getBitsUint8 (6) : 0; } } void layer1Step2 (SideInfoLayer1& si, float fraction[2][32]) noexcept { if (frame.numChannels == 2) { int i, jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : 32; for (i = 0; i < jsbound; ++i) { const uint8 n0 = si.allocation[i][0]; const uint8 n1 = si.allocation[i][1]; fraction[0][i] = n0 > 0 ? (float) (((-1 << n0) + getBitsUint16 (n0 + 1) + 1) * constants.muls[n0 + 1][si.scaleFactor[i][0]]) : 0; fraction[1][i] = n1 > 0 ? (float) (((-1 << n1) + getBitsUint16 (n1 + 1) + 1) * constants.muls[n1 + 1][si.scaleFactor[i][1]]) : 0; } for (i = jsbound; i < 32; ++i) { const uint8 n = si.allocation[i][0]; if (n > 0) { const uint32 w = ((uint32) (-1 << n) + getBitsUint16 (n + 1) + 1); fraction[0][i] = (float) (w * constants.muls[n + 1][si.scaleFactor[i][0]]); fraction[1][i] = (float) (w * constants.muls[n + 1][si.scaleFactor[i][1]]); } else fraction[0][i] = fraction[1][i] = 0; } } else { for (int i = 0; i < 32; ++i) { const uint8 n = si.allocation[i][0]; const uint8 j = si.scaleFactor[i][0]; if (n > 0) fraction[0][i] = (float) (((-1 << n) + getBitsUint16 (n + 1) + 1) * constants.muls[n + 1][j]); else fraction[0][i] = 0; } } } void layer2Step1 (SideInfoLayer2& si) noexcept { zerostruct (si); const int sblimit = frame.layer2SubBandLimit; const int jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : frame.layer2SubBandLimit; const AllocationTable* allocTable = frame.allocationTable; uint8 scfsi[32][2]; if (frame.numChannels == 2) { for (int i = 0; i < jsbound; ++i) { const int16 step = allocTable->bits; allocTable += (1 << step); si.allocation[i][0] = getBitsUint8 (step); si.allocation[i][1] = getBitsUint8 (step); } for (int i = jsbound; i < sblimit; ++i) { const int16 step = allocTable->bits; const uint8 b0 = getBitsUint8 (step); allocTable += (1 << step); si.allocation[i][0] = b0; si.allocation[i][1] = b0; } for (int i = 0; i < sblimit; ++i) { scfsi[i][0] = si.allocation[i][0] ? getBitsUint8 (2) : 0; scfsi[i][1] = si.allocation[i][1] ? getBitsUint8 (2) : 0; } } else { for (int i = 0; i < sblimit; ++i) { const int16 step = allocTable->bits; allocTable += (1 << step); si.allocation[i][0] = getBitsUint8 (step); } for (int i = 0; i < sblimit; ++i) scfsi[i][0] = si.allocation[i][0] ? getBitsUint8 (2) : 0; } for (int i = 0; i < sblimit; ++i) { for (int ch = 0; ch < frame.numChannels; ++ch) { uint8 s0 = 0, s1 = 0, s2 = 0; if (si.allocation[i][ch]) { switch (scfsi[i][ch]) { case 0: s0 = getBitsUint8 (6); s1 = getBitsUint8 (6); s2 = getBitsUint8 (6); break; case 1: s1 = s0 = getBitsUint8 (6); s2 = getBitsUint8 (6); break; case 2: s2 = s1 = s0 = getBitsUint8 (6); break; case 3: s0 = getBitsUint8 (6); s2 = s1 = getBitsUint8 (6); break; default: break; } } si.scaleFactor[i][ch][0] = s0; si.scaleFactor[i][ch][1] = s1; si.scaleFactor[i][ch][2] = s2; } } } void layer2Step2 (SideInfoLayer2& si, const int gr, float fraction[2][4][32]) noexcept { const AllocationTable* allocTable = frame.allocationTable; const int jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : frame.layer2SubBandLimit; for (int i = 0; i < jsbound; ++i) { const int16 step = allocTable->bits; for (int ch = 0; ch < frame.numChannels; ++ch) { const uint8 ba = si.allocation[i][ch]; if (ba != 0) { const uint8 x1 = jmin ((uint8) 63, si.scaleFactor[i][ch][gr]); const AllocationTable* const alloc2 = allocTable + ba; const int16 k = jmin ((int16) 16, alloc2->bits); const int16 d1 = alloc2->d; if (d1 < 0) { const double cm = constants.muls[k][x1]; fraction[ch][0][i] = (float) (((int) getBits (k) + d1) * cm); fraction[ch][1][i] = (float) (((int) getBits (k) + d1) * cm); fraction[ch][2][i] = (float) (((int) getBits (k) + d1) * cm); } else { const uint8* const tab = constants.getGroupTable (d1, getBits (k)); fraction[ch][0][i] = (float) constants.muls[tab[0]][x1]; fraction[ch][1][i] = (float) constants.muls[tab[1]][x1]; fraction[ch][2][i] = (float) constants.muls[tab[2]][x1]; } } else { fraction[ch][0][i] = fraction[ch][1][i] = fraction[ch][2][i] = 0; } } allocTable += (1 << step); } for (int i = jsbound; i < frame.layer2SubBandLimit; ++i) { const int16 step = allocTable->bits; const uint8 ba = si.allocation[i][0]; if (ba != 0) { const AllocationTable* const alloc2 = allocTable + ba; int16 k = alloc2->bits; int16 d1 = alloc2->d; k = (k <= 16) ? k : 16; if (d1 < 0) { const int v0 = (int) getBits (k); const int v1 = (int) getBits (k); const int v2 = (int) getBits (k); for (int ch = 0; ch < frame.numChannels; ++ch) { const uint8 x1 = jmin ((uint8) 63, si.scaleFactor[i][ch][gr]); const double cm = constants.muls[k][x1]; fraction[ch][0][i] = (float) ((v0 + d1) * cm); fraction[ch][1][i] = (float) ((v1 + d1) * cm); fraction[ch][2][i] = (float) ((v2 + d1) * cm); } } else { const uint8* const tab = constants.getGroupTable (d1, getBits (k)); const uint8 k0 = tab[0]; const uint8 k1 = tab[1]; const uint8 k2 = tab[2]; for (int ch = 0; ch < frame.numChannels; ++ch) { const uint8 x1 = jmin ((uint8) 63, si.scaleFactor[i][ch][gr]); fraction[ch][0][i] = (float) constants.muls[k0][x1]; fraction[ch][1][i] = (float) constants.muls[k1][x1]; fraction[ch][2][i] = (float) constants.muls[k2][x1]; } } } else { fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] = 0; fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0; } allocTable += (1 << step); } for (int ch = 0; ch < frame.numChannels; ++ch) for (int i = frame.layer2SubBandLimit; i < 32; ++i) fraction[ch][0][i] = fraction[ch][1][i] = fraction[ch][2][i] = 0; } void getLayer3SideInfo1 (const int stereo, const bool msStereo, const int sampleRate, const int single) noexcept { const int powdiff = (single == 3) ? 4 : 0; sideinfo.mainDataStart = getBits (9); sideinfo.privateBits = getBitsUnchecked (stereo == 1 ? 5 : 3); for (int ch = 0; ch < stereo; ++ch) { sideinfo.ch[ch].gr[0].scfsi = -1; sideinfo.ch[ch].gr[1].scfsi = (int) getBitsUnchecked (4); } for (int gr = 0; gr < 2; ++gr) { for (int ch = 0; ch < stereo; ++ch) { Layer3SideInfo::Info& granule = sideinfo.ch[ch].gr[gr]; granule.part2_3Length = getBits (12); granule.bigValues = jmin (288u, getBitsUnchecked (9)); const int qss = (int) getBitsUnchecked (8); granule.pow2gain = constants.powToGains + 256 - qss + powdiff; if (msStereo) granule.pow2gain += 2; granule.scaleFactorCompression = getBitsUnchecked (4); if (getOneBit()) { granule.blockType = getBitsUnchecked (2); granule.mixedBlockFlag = getOneBit(); granule.tableSelect[0] = getBitsUnchecked (5); granule.tableSelect[1] = getBitsUnchecked (5); granule.tableSelect[2] = 0; for (int i = 0; i < 3; ++i) { const uint32 sbg = (getBitsUnchecked (3) << 3); granule.fullGain[i] = granule.pow2gain + sbg; } granule.region1Start = 36 >> 1; granule.region2Start = 576 >> 1; } else { for (int i = 0; i < 3; ++i) granule.tableSelect[i] = getBitsUnchecked (5); const int r0c = (int) getBitsUnchecked (4); const int r1c = (int) getBitsUnchecked (3); const int region0index = jmin (22, r0c + 1); const int region1index = jmin (22, r0c + 1 + r1c + 1); granule.region1Start = (uint32) (bandInfo[sampleRate].longIndex[region0index] >> 1); granule.region2Start = (uint32) (bandInfo[sampleRate].longIndex[region1index] >> 1); granule.blockType = 0; granule.mixedBlockFlag = 0; } granule.preflag = getOneBit(); granule.scaleFactorScale = getOneBit(); granule.count1TableSelect = getOneBit(); } } } void getLayer3SideInfo2 (const int stereo, const bool msStereo, const int sampleRate, const int single) noexcept { const int powdiff = (single == 3) ? 4 : 0; sideinfo.mainDataStart = getBits (8); sideinfo.privateBits = stereo == 1 ? getOneBit() : getBitsUnchecked (2); for (int ch = 0; ch < stereo; ++ch) { Layer3SideInfo::Info& granule = sideinfo.ch[ch].gr[0]; granule.part2_3Length = getBits (12); granule.bigValues = jmin (288u, getBitsUnchecked (9)); const uint32 qss = getBitsUnchecked (8); granule.pow2gain = constants.powToGains + 256 - qss + powdiff; if (msStereo) granule.pow2gain += 2; granule.scaleFactorCompression = getBits (9); if (getOneBit()) { granule.blockType = getBitsUnchecked (2); granule.mixedBlockFlag = getOneBit(); granule.tableSelect[0] = getBitsUnchecked (5); granule.tableSelect[1] = getBitsUnchecked (5); granule.tableSelect[2] = 0; for (int i = 0; i < 3; ++i) { const uint32 sbg = (getBitsUnchecked (3) << 3); granule.fullGain[i] = granule.pow2gain + sbg; } if (granule.blockType == 0) {} if (granule.blockType == 2) granule.region1Start = sampleRate == 8 ? 36 : (36 >> 1); else granule.region1Start = sampleRate == 8 ? (108 >> 1) : (54 >> 1); granule.region2Start = 576 >> 1; } else { for (int i = 0; i < 3; ++i) granule.tableSelect[i] = getBitsUnchecked (5); const int r0c = (int) getBitsUnchecked (4); const int r1c = (int) getBitsUnchecked (3); const int region0index = jmin (22, r0c + 1); const int region1index = jmin (22, r0c + 1 + r1c + 1); granule.region1Start = (uint32) (bandInfo[sampleRate].longIndex[region0index] >> 1); granule.region2Start = (uint32) (bandInfo[sampleRate].longIndex[region1index] >> 1); granule.blockType = 0; granule.mixedBlockFlag = 0; } granule.scaleFactorScale = getOneBit(); granule.count1TableSelect = getOneBit(); } } int getLayer3ScaleFactors1 (int* scf, const Layer3SideInfo::Info& granule) noexcept { static const uint8 lengths[2][16] = { { 0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 }, { 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3 } }; int numBits; const int num0 = lengths[0][granule.scaleFactorCompression]; const int num1 = lengths[1][granule.scaleFactorCompression]; if (granule.blockType == 2) { int i = 18; numBits = (num0 + num1) * 18; if (granule.mixedBlockFlag) { for (int j = 8; --j >= 0;) *scf++ = (int) getBitsUnchecked (num0); numBits -= num0; i = 9; } for (; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); for (i = 18; --i >= 0;) *scf++ = (int) getBitsUnchecked (num1); *scf++ = 0; *scf++ = 0; *scf++ = 0; } else { const int scfsi = granule.scfsi; if (scfsi < 0) { for (int i = 11; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); for (int j = 10; --j >= 0;) *scf++ = (int) getBitsUnchecked (num1); numBits = (num0 + num1) * 10 + num0; } else { numBits = 0; if ((scfsi & 8) == 0) { for (int i = 6; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); numBits += num0 * 6; } else scf += 6; if ((scfsi & 4) == 0) { for (int i = 5; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); numBits += num0 * 5; } else scf += 5; if ((scfsi & 2) == 0) { for (int i = 5; --i >= 0;) *scf++ = (int) getBitsUnchecked (num1); numBits += num1 * 5; } else scf += 5; if ((scfsi & 1) == 0) { for (int i = 5; --i >= 0;) *scf++ = (int) getBitsUnchecked (num1); numBits += num1 * 5; } else scf += 5; } *scf = 0; } return numBits; } int getLayer3ScaleFactors2 (int* scf, Layer3SideInfo::Info& granule, const bool iStereo) noexcept { static const uint8 scaleTable[3][6][4] = { { { 6, 5, 5, 5 }, { 6, 5, 7, 3 }, { 11, 10, 0, 0 }, { 7, 7, 7, 0 }, { 6, 6, 6, 3 }, { 8, 8, 5, 0 } }, { { 9, 9, 9, 9 }, { 9, 9, 12, 6 }, { 18, 18, 0, 0 }, { 12, 12, 12, 0 }, { 12, 9, 9, 6 }, { 15, 12, 9, 0 } }, { { 6, 9, 9, 9 }, { 6, 9, 12, 6 }, { 15, 18, 0, 0 }, { 6, 15, 12, 0 }, { 6, 12, 9, 6 }, { 6, 18, 9, 0 } } }; uint32 len = iStereo ? constants.iLength2 [granule.scaleFactorCompression >> 1] : constants.nLength2 [granule.scaleFactorCompression]; granule.preflag = (len >> 15) & 1; int n = 0; if (granule.blockType == 2) { ++n; if (granule.mixedBlockFlag) ++n; } const uint8* const data = scaleTable[n][(len >> 12) & 7]; int i, numBits = 0; for (i = 0; i < 4; ++i) { int num = len & 7; len >>= 3; if (num) { for (int j = 0; j < (int) (data[i]); ++j) *scf++ = (int) getBitsUnchecked (num); numBits += data[i] * num; } else { for (int j = 0; j < (int) (data[i]); ++j) *scf++ = 0; } } n = (n << 1) + 1; for (i = 0; i < n; ++i) *scf++ = 0; return numBits; } bool layer3DequantizeSample (float xr[32][18], int* scf, Layer3SideInfo::Info& granule, int sampleRate, int part2bits) noexcept { const uint32 shift = 1 + granule.scaleFactorScale; float* xrpnt = (float*) xr; int part2remain = (int) granule.part2_3Length - part2bits; zeromem (xrpnt, sizeof (float) * (size_t) (&xr[32][0] - xrpnt)); const int bv = (int) granule.bigValues; const int region1 = (int) granule.region1Start; const int region2 = (int) granule.region2Start; int l3 = ((576 >> 1) - bv) >> 1; int l[3]; if (bv <= region1) { l[0] = bv; l[1] = 0; l[2] = 0; } else { l[0] = region1; if (bv <= region2) { l[1] = bv - l[0]; l[2] = 0; } else { l[1] = region2 - l[0]; l[2] = bv - region2; } } for (int i = 0; i < 3; ++i) if (l[i] < 0) l[i] = 0; if (granule.blockType == 2) { int max[4]; int step = 0, lwin = 0, cb = 0, mc = 0; float v = 0; int* map; int* mapEnd; if (granule.mixedBlockFlag) { max[3] = -1; max[0] = max[1] = max[2] = 2; map = constants.map [sampleRate][0]; mapEnd = constants.mapEnd [sampleRate][0]; } else { max[0] = max[1] = max[2] = max[3] = -1; map = constants.map [sampleRate][1]; mapEnd = constants.mapEnd [sampleRate][1]; } for (int i = 0; i < 2; ++i) { const BitsToTableMap* h = huffmanTables1 + granule.tableSelect[i]; for (int lp = l[i]; lp != 0; --lp, --mc) { int x, y; if (mc == 0) { mc = *map++; xrpnt = ((float*) xr) + (*map++); lwin = *map++; cb = *map++; if (lwin == 3) { v = granule.pow2gain[ (*scf++) << shift]; step = 1; } else { v = granule.fullGain[lwin][ (*scf++) << shift]; step = 3; } } const int16* val = h->table; while ((y = *val++) < 0) { if (getOneBit()) val -= y; --part2remain; } x = y >> 4; y &= 15; if (x == 15) { max[lwin] = cb; part2remain -= h->bits + 1; x += getBits ((int) h->bits); *xrpnt = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); } else if (x) { max[lwin] = cb; *xrpnt = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); --part2remain; } else *xrpnt = 0; xrpnt += step; if (y == 15) { max[lwin] = cb; part2remain -= h->bits + 1; y += getBits ((int) h->bits); *xrpnt = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); } else if (y) { max[lwin] = cb; *xrpnt = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); --part2remain; } else *xrpnt = 0; xrpnt += step; } } for (; l3 && (part2remain > 0); --l3) { const BitsToTableMap* h = huffmanTables2 + granule.count1TableSelect; const int16* val = h->table; int16 a; while ((a = *val++) < 0) { if (part2remain <= 0) { a = 0; break; } --part2remain; if (getOneBit()) val -= a; } for (int i = 0; i < 4; ++i) { if ((i & 1) == 0) { if (mc == 0) { mc = *map++; xrpnt = ((float*) xr) + (*map++); lwin = *map++; cb = *map++; if (lwin == 3) { v = granule.pow2gain[ (*scf++) << shift]; step = 1; } else { v = granule.fullGain[lwin][ (*scf++) << shift]; step = 3; } } --mc; } if ((a & (8 >> i))) { max[lwin] = cb; if (part2remain == 0) break; --part2remain; *xrpnt = getOneBit() ? -v : v; } else *xrpnt = 0; xrpnt += step; } } while (map < mapEnd) { if (mc == 0) { mc = *map++; xrpnt = ((float*) xr) + *map++; step = (*map++ == 3) ? 1 : 3; ++map; } --mc; *xrpnt = 0; xrpnt += step; *xrpnt = 0; xrpnt += step; } granule.maxBand[0] = (uint32) (max[0] + 1); granule.maxBand[1] = (uint32) (max[1] + 1); granule.maxBand[2] = (uint32) (max[2] + 1); granule.maxBandl = (uint32) (max[3] + 1); const int rmax = jmax (max[0], max[1], max[3]) + 1; granule.maxb = rmax ? (uint32) constants.shortLimit[sampleRate][rmax] : (uint32) constants.longLimit[sampleRate][max[3] + 1]; } else { static const int pretab1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0 }; static const int pretab2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; const int* pretab = (const int*) (granule.preflag ? pretab1 : pretab2); int max = -1, cb = 0, mc = 0; int* map = constants.map [sampleRate][2]; float v = 0; for (int i = 0; i < 3; ++i) { const BitsToTableMap* h = huffmanTables1 + granule.tableSelect[i]; for (int lp = l[i]; lp != 0; --lp, --mc) { if (mc == 0) { mc = *map++; v = granule.pow2gain [((*scf++) + (*pretab++)) << shift]; cb = *map++; } const int16* val = h->table; int y; while ((y = *val++) < 0) { if (getOneBit()) val -= y; --part2remain; } int x = y >> 4; y &= 15; if (x == 15) { max = cb; part2remain -= h->bits + 1; x += getBits ((int) h->bits); *xrpnt++ = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); } else if (x) { max = cb; *xrpnt++ = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); --part2remain; } else *xrpnt++ = 0; if (y == 15) { max = cb; part2remain -= h->bits + 1; y += getBits ((int) h->bits); *xrpnt++ = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); } else if (y) { max = cb; *xrpnt++ = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); --part2remain; } else *xrpnt++ = 0; } } for (; l3 && part2remain > 0; --l3) { const BitsToTableMap* h = huffmanTables2 + granule.count1TableSelect; const int16* values = h->table; int16 a; while ((a = *values++) < 0) { if (part2remain <= 0) { a = 0; break; } --part2remain; if (getOneBit()) values -= a; } for (int i = 0; i < 4; ++i) { if ((i & 1) == 0) { if (mc == 0) { mc = *map++; cb = *map++; v = granule.pow2gain [((*scf++) + (*pretab++)) << shift]; } --mc; } if ((a & (0x8 >> i))) { max = cb; if (part2remain <= 0) break; --part2remain; *xrpnt++ = getOneBit() ? -v : v; } else *xrpnt++ = 0; } } zeromem (xrpnt, sizeof (float) * (size_t) (&xr[32][0] - xrpnt)); granule.maxBandl = (uint32) (max + 1); granule.maxb = (uint32) constants.longLimit[sampleRate][granule.maxBandl]; } while (part2remain > 16) { getBits (16); part2remain -= 16; } if (part2remain > 0) getBits (part2remain); else if (part2remain < 0) return true; return false; } void layer3Hybrid (float fsIn[32][18], float tsOut[18][32], int ch, const Layer3SideInfo::Info& granule) noexcept { float* ts = (float*) tsOut; float* rawout1, *rawout2; int sb = 0; { int b = hybridBlockIndex[ch]; rawout1 = hybridBlock[b][ch]; b = 1 - b; rawout2 = hybridBlock[b][ch]; hybridBlockIndex[ch] = b; } if (granule.mixedBlockFlag) { sb = 2; DCT::dct36 (fsIn[0], rawout1, rawout2, constants.win[0], ts); DCT::dct36 (fsIn[1], rawout1 + 18, rawout2 + 18, constants.win1[0], ts + 1); rawout1 += 36; rawout2 += 36; ts += 2; } const uint32 bt = granule.blockType; if (bt == 2) { for (; sb < (int) granule.maxb; sb += 2, ts += 2, rawout1 += 36, rawout2 += 36) { DCT::dct12 (fsIn[sb], rawout1, rawout2, constants.win[2], ts); DCT::dct12 (fsIn[sb + 1], rawout1 + 18, rawout2 + 18, constants.win1[2], ts + 1); } } else { for (; sb < (int) granule.maxb; sb += 2, ts += 2, rawout1 += 36, rawout2 += 36) { DCT::dct36 (fsIn[sb], rawout1, rawout2, constants.win[bt], ts); DCT::dct36 (fsIn[sb + 1], rawout1 + 18, rawout2 + 18, constants.win1[bt], ts + 1); } } for (; sb < 32; ++sb, ++ts) { for (int i = 0; i < 18; ++i) { ts[i * 32] = *rawout1++; *rawout2++ = 0; } } } void synthesiseStereo (const float* bandPtr0, const float* bandPtr1, float* out0, float* out1, int& samplesDone) noexcept { int dummy = samplesDone; synthesise (bandPtr0, 0, out0, dummy); synthesise (bandPtr1, 1, out1, samplesDone); } void synthesise (const float* bandPtr, const int channel, float* out, int& samplesDone) { out += samplesDone; const int bo = channel == 0 ? ((synthBo - 1) & 15) : synthBo; float (*buf)[0x110] = synthBuffers[channel]; float* b0; int j, bo1 = bo; if (bo & 1) { b0 = buf[0]; DCT::dct64 (buf[1] + ((bo + 1) & 15), buf[0] + bo, bandPtr); } else { ++bo1; b0 = buf[1]; DCT::dct64 (buf[0] + bo, buf[1] + bo1, bandPtr); } synthBo = bo; const float* window = constants.decodeWin + 16 - bo1; for (j = 16; j != 0; --j, b0 += 16, window += 32) { float sum = window[0] * b0[0]; sum -= window[1] * b0[1]; sum += window[2] * b0[2]; sum -= window[3] * b0[3]; sum += window[4] * b0[4]; sum -= window[5] * b0[5]; sum += window[6] * b0[6]; sum -= window[7] * b0[7]; sum += window[8] * b0[8]; sum -= window[9] * b0[9]; sum += window[10] * b0[10]; sum -= window[11] * b0[11]; sum += window[12] * b0[12]; sum -= window[13] * b0[13]; sum += window[14] * b0[14]; sum -= window[15] * b0[15]; *out++ = sum; } { float sum = window[0] * b0[0]; sum += window[2] * b0[2]; sum += window[4] * b0[4]; sum += window[6] * b0[6]; sum += window[8] * b0[8]; sum += window[10] * b0[10]; sum += window[12] * b0[12]; sum += window[14] * b0[14]; *out++ = sum; b0 -= 16; window -= 32; window += bo1 << 1; } for (j = 15; j != 0; --j, b0 -= 16, window -= 32) { float sum = -window[-1] * b0[0]; sum -= window[-2] * b0[1]; sum -= window[-3] * b0[2]; sum -= window[-4] * b0[3]; sum -= window[-5] * b0[4]; sum -= window[-6] * b0[5]; sum -= window[-7] * b0[6]; sum -= window[-8] * b0[7]; sum -= window[-9] * b0[8]; sum -= window[-10] * b0[9]; sum -= window[-11] * b0[10]; sum -= window[-12] * b0[11]; sum -= window[-13] * b0[12]; sum -= window[-14] * b0[13]; sum -= window[-15] * b0[14]; sum -= window[0] * b0[15]; *out++ = sum; } samplesDone += 32; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MP3Stream) }; //============================================================================== static const char* const mp3FormatName = "MP3 file"; //============================================================================== class MP3Reader : public AudioFormatReader { public: MP3Reader (InputStream* const in) : AudioFormatReader (in, mp3FormatName), stream (*in), currentPosition (0), decodedStart (0), decodedEnd (0) { skipID3(); const int64 streamPos = stream.stream.getPosition(); if (readNextBlock()) { bitsPerSample = 32; usesFloatingPointData = true; sampleRate = stream.frame.getFrequency(); numChannels = (unsigned int) stream.frame.numChannels; lengthInSamples = findLength (streamPos); } } bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { jassert (destSamples != nullptr); if (currentPosition != startSampleInFile) { if (! stream.seek ((int) (startSampleInFile / 1152 - 1))) { currentPosition = -1; createEmptyDecodedData(); } else { decodedStart = decodedEnd = 0; const int64 streamPos = stream.currentFrameIndex * 1152; int toSkip = (int) (startSampleInFile - streamPos); jassert (toSkip >= 0); while (toSkip > 0) { if (! readNextBlock()) { createEmptyDecodedData(); break; } const int numReady = decodedEnd - decodedStart; if (numReady > toSkip) { decodedStart += toSkip; break; } toSkip -= numReady; } currentPosition = startSampleInFile; } } while (numSamples > 0) { if (decodedEnd <= decodedStart && ! readNextBlock()) { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (float) * (size_t) numSamples); return false; } const int numToCopy = jmin (decodedEnd - decodedStart, numSamples); float* const* const dst = reinterpret_cast (destSamples); memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, sizeof (float) * (size_t) numToCopy); if (numDestChannels > 1 && dst[1] != nullptr) memcpy (dst[1] + startOffsetInDestBuffer, (numChannels < 2 ? decoded0 : decoded1) + decodedStart, sizeof (float) * (size_t) numToCopy); startOffsetInDestBuffer += numToCopy; decodedStart += numToCopy; currentPosition += numToCopy; numSamples -= numToCopy; } return true; } private: MP3Stream stream; int64 currentPosition; enum { decodedDataSize = 1152 }; float decoded0 [decodedDataSize], decoded1 [decodedDataSize]; int decodedStart, decodedEnd; void createEmptyDecodedData() noexcept { zeromem (decoded0, sizeof (decoded0)); zeromem (decoded1, sizeof (decoded1)); decodedStart = 0; decodedEnd = decodedDataSize; } bool readNextBlock() { for (int attempts = 10; --attempts >= 0;) { int samplesDone = 0; const int result = stream.decodeNextBlock (decoded0, decoded1, samplesDone); if (result > 0 && stream.stream.isExhausted()) { createEmptyDecodedData(); return true; } if (result <= 0) { decodedStart = 0; decodedEnd = samplesDone; return result == 0; } } return false; } void skipID3() { const int64 originalPosition = stream.stream.getPosition(); const uint32 firstWord = (uint32) stream.stream.readInt(); if ((firstWord & 0xffffff) == 0x334449) { uint8 buffer[6]; if (stream.stream.read (buffer, 6) == 6 && buffer[0] != 0xff && ((buffer[2] | buffer[3] | buffer[4] | buffer[5]) & 0x80) == 0) { const uint32 length = (((uint32) buffer[2]) << 21) | (((uint32) buffer[3]) << 14) | (((uint32) buffer[4]) << 7) | ((uint32) buffer[5]); stream.stream.skipNextBytes (length); return; } } stream.stream.setPosition (originalPosition); } int64 findLength (int64 streamStartPos) { int64 numFrames = stream.numFrames; if (numFrames <= 0) { const int64 streamSize = stream.stream.getTotalLength(); if (streamSize > 0) { const int bytesPerFrame = stream.frame.frameSize + 4; if (bytesPerFrame == 417 || bytesPerFrame == 418) numFrames = roundToInt ((streamSize - streamStartPos) / 417.95918); // more accurate for 128k else numFrames = (streamSize - streamStartPos) / bytesPerFrame; } } return numFrames * 1152; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MP3Reader) }; } //============================================================================== MP3AudioFormat::MP3AudioFormat() : AudioFormat (MP3Decoder::mp3FormatName, ".mp3") {} MP3AudioFormat::~MP3AudioFormat() {} Array MP3AudioFormat::getPossibleSampleRates() { return Array(); } Array MP3AudioFormat::getPossibleBitDepths() { return Array(); } bool MP3AudioFormat::canDoStereo() { return true; } bool MP3AudioFormat::canDoMono() { return true; } bool MP3AudioFormat::isCompressed() { return true; } StringArray MP3AudioFormat::getQualityOptions() { return StringArray(); } AudioFormatReader* MP3AudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) { ScopedPointer r (new MP3Decoder::MP3Reader (sourceStream)); if (r->lengthInSamples > 0) return r.release(); if (! deleteStreamIfOpeningFails) r->input = nullptr; return nullptr; } AudioFormatWriter* MP3AudioFormat::createWriterFor (OutputStream*, double /*sampleRateToUse*/, unsigned int /*numberOfChannels*/, int /*bitsPerSample*/, const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/) { return nullptr; } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h000066400000000000000000000054661320201440200326320ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_MP3AUDIOFORMAT || DOXYGEN //============================================================================== /** Software-based MP3 decoding format (doesn't currently provide an encoder). IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile the MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing that ROLI Ltd. is in no way responsible for any patent, copyright, or other legal issues that you may suffer as a result. The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party intellectual property. If you wish to use it, please seek your own independent advice about the legality of doing so. If you are not willing to accept full responsibility for the consequences of using this code, then do not enable the JUCE_USE_MP3AUDIOFORMAT setting. */ class MP3AudioFormat : public AudioFormat { public: //============================================================================== MP3AudioFormat(); ~MP3AudioFormat(); //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; bool isCompressed() override; StringArray getQualityOptions() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; }; #endif juce_OggVorbisAudioFormat.cpp000066400000000000000000000447201320201440200344040ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_OGGVORBIS #if JUCE_MAC && ! defined (__MACOSX__) #define __MACOSX__ 1 #endif namespace OggVorbisNamespace { #if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE) #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365) #endif #if JUCE_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #include "oggvorbis/vorbisenc.h" #include "oggvorbis/codec.h" #include "oggvorbis/vorbisfile.h" #include "oggvorbis/bitwise.c" #include "oggvorbis/framing.c" #include "oggvorbis/libvorbis-1.3.2/lib/analysis.c" #include "oggvorbis/libvorbis-1.3.2/lib/bitrate.c" #include "oggvorbis/libvorbis-1.3.2/lib/block.c" #include "oggvorbis/libvorbis-1.3.2/lib/codebook.c" #include "oggvorbis/libvorbis-1.3.2/lib/envelope.c" #include "oggvorbis/libvorbis-1.3.2/lib/floor0.c" #include "oggvorbis/libvorbis-1.3.2/lib/floor1.c" #include "oggvorbis/libvorbis-1.3.2/lib/info.c" #include "oggvorbis/libvorbis-1.3.2/lib/lpc.c" #include "oggvorbis/libvorbis-1.3.2/lib/lsp.c" #include "oggvorbis/libvorbis-1.3.2/lib/mapping0.c" #include "oggvorbis/libvorbis-1.3.2/lib/mdct.c" #include "oggvorbis/libvorbis-1.3.2/lib/psy.c" #include "oggvorbis/libvorbis-1.3.2/lib/registry.c" #include "oggvorbis/libvorbis-1.3.2/lib/res0.c" #include "oggvorbis/libvorbis-1.3.2/lib/sharedbook.c" #include "oggvorbis/libvorbis-1.3.2/lib/smallft.c" #include "oggvorbis/libvorbis-1.3.2/lib/synthesis.c" #include "oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c" #include "oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c" #include "oggvorbis/libvorbis-1.3.2/lib/window.c" #if JUCE_MSVC #pragma warning (pop) #endif #if JUCE_CLANG #pragma clang diagnostic pop #endif #else #include #include #include #endif } #undef max #undef min //============================================================================== static const char* const oggFormatName = "Ogg-Vorbis file"; const char* const OggVorbisAudioFormat::encoderName = "encoder"; const char* const OggVorbisAudioFormat::id3title = "id3title"; const char* const OggVorbisAudioFormat::id3artist = "id3artist"; const char* const OggVorbisAudioFormat::id3album = "id3album"; const char* const OggVorbisAudioFormat::id3comment = "id3comment"; const char* const OggVorbisAudioFormat::id3date = "id3date"; const char* const OggVorbisAudioFormat::id3genre = "id3genre"; const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber"; //============================================================================== class OggReader : public AudioFormatReader { public: OggReader (InputStream* const inp) : AudioFormatReader (inp, oggFormatName), reservoirStart (0), samplesInReservoir (0) { using namespace OggVorbisNamespace; sampleRate = 0; usesFloatingPointData = true; callbacks.read_func = &oggReadCallback; callbacks.seek_func = &oggSeekCallback; callbacks.close_func = &oggCloseCallback; callbacks.tell_func = &oggTellCallback; const int err = ov_open_callbacks (input, &ovFile, 0, 0, callbacks); if (err == 0) { vorbis_info* info = ov_info (&ovFile, -1); vorbis_comment* const comment = ov_comment (&ovFile, -1); addMetadataItem (comment, "ENCODER", OggVorbisAudioFormat::encoderName); addMetadataItem (comment, "TITLE", OggVorbisAudioFormat::id3title); addMetadataItem (comment, "ARTIST", OggVorbisAudioFormat::id3artist); addMetadataItem (comment, "ALBUM", OggVorbisAudioFormat::id3album); addMetadataItem (comment, "COMMENT", OggVorbisAudioFormat::id3comment); addMetadataItem (comment, "DATE", OggVorbisAudioFormat::id3date); addMetadataItem (comment, "GENRE", OggVorbisAudioFormat::id3genre); addMetadataItem (comment, "TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber); lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1); numChannels = (unsigned int) info->channels; bitsPerSample = 16; sampleRate = info->rate; reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096)); } } ~OggReader() { OggVorbisNamespace::ov_clear (&ovFile); } void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment, const char* name, const char* metadataName) { if (const char* value = vorbis_comment_query (comment, name, 0)) metadataValues.set (metadataName, value); } //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { while (numSamples > 0) { const int numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile); if (startSampleInFile >= reservoirStart && numAvailable > 0) { // got a few samples overlapping, so use them before seeking.. const int numToUse = jmin (numSamples, numAvailable); for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), sizeof (float) * (size_t) numToUse); startSampleInFile += numToUse; numSamples -= numToUse; startOffsetInDestBuffer += numToUse; if (numSamples == 0) break; } if (startSampleInFile < reservoirStart || startSampleInFile + numSamples > reservoirStart + samplesInReservoir) { // buffer miss, so refill the reservoir int bitStream = 0; reservoirStart = jmax (0, (int) startSampleInFile); samplesInReservoir = reservoir.getNumSamples(); if (reservoirStart != (int) OggVorbisNamespace::ov_pcm_tell (&ovFile)) OggVorbisNamespace::ov_pcm_seek (&ovFile, reservoirStart); int offset = 0; int numToRead = samplesInReservoir; while (numToRead > 0) { float** dataIn = nullptr; const long samps = OggVorbisNamespace::ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); if (samps <= 0) break; jassert (samps <= numToRead); for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;) memcpy (reservoir.getWritePointer (i, offset), dataIn[i], sizeof (float) * (size_t) samps); numToRead -= samps; offset += samps; } if (numToRead > 0) reservoir.clear (offset, numToRead); } } if (numSamples > 0) { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); } return true; } //============================================================================== static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource) { return (size_t) (static_cast (datasource)->read (ptr, (int) (size * nmemb))) / size; } static int oggSeekCallback (void* datasource, OggVorbisNamespace::ogg_int64_t offset, int whence) { InputStream* const in = static_cast (datasource); if (whence == SEEK_CUR) offset += in->getPosition(); else if (whence == SEEK_END) offset += in->getTotalLength(); in->setPosition (offset); return 0; } static int oggCloseCallback (void*) { return 0; } static long oggTellCallback (void* datasource) { return (long) static_cast (datasource)->getPosition(); } private: OggVorbisNamespace::OggVorbis_File ovFile; OggVorbisNamespace::ov_callbacks callbacks; AudioSampleBuffer reservoir; int reservoirStart, samplesInReservoir; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader) }; //============================================================================== class OggWriter : public AudioFormatWriter { public: OggWriter (OutputStream* const out, const double sampleRate_, const unsigned int numChannels_, const unsigned int bitsPerSample_, const int qualityIndex, const StringPairArray& metadata) : AudioFormatWriter (out, oggFormatName, sampleRate_, numChannels_, bitsPerSample_), ok (false) { using namespace OggVorbisNamespace; vorbis_info_init (&vi); if (vorbis_encode_init_vbr (&vi, (int) numChannels_, (int) sampleRate_, jlimit (0.0f, 1.0f, qualityIndex * 0.1f)) == 0) { vorbis_comment_init (&vc); addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER"); addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE"); addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST"); addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM"); addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT"); addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE"); addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE"); addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber, "TRACKNUMBER"); vorbis_analysis_init (&vd, &vi); vorbis_block_init (&vd, &vb); ogg_stream_init (&os, Random::getSystemRandom().nextInt()); ogg_packet header; ogg_packet header_comm; ogg_packet header_code; vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code); ogg_stream_packetin (&os, &header); ogg_stream_packetin (&os, &header_comm); ogg_stream_packetin (&os, &header_code); for (;;) { if (ogg_stream_flush (&os, &og) == 0) break; output->write (og.header, (size_t) og.header_len); output->write (og.body, (size_t) og.body_len); } ok = true; } } ~OggWriter() { using namespace OggVorbisNamespace; if (ok) { // write a zero-length packet to show ogg that we're finished.. writeSamples (0); ogg_stream_clear (&os); vorbis_block_clear (&vb); vorbis_dsp_clear (&vd); vorbis_comment_clear (&vc); vorbis_info_clear (&vi); output->flush(); } else { vorbis_info_clear (&vi); output = nullptr; // to stop the base class deleting this, as it needs to be returned // to the caller of createWriter() } } //============================================================================== bool write (const int** samplesToWrite, int numSamples) override { if (ok) { using namespace OggVorbisNamespace; if (numSamples > 0) { const double gain = 1.0 / 0x80000000u; float** const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples); for (int i = (int) numChannels; --i >= 0;) { float* const dst = vorbisBuffer[i]; const int* const src = samplesToWrite [i]; if (src != nullptr && dst != nullptr) { for (int j = 0; j < numSamples; ++j) dst[j] = (float) (src[j] * gain); } } } writeSamples (numSamples); } return ok; } void writeSamples (int numSamples) { using namespace OggVorbisNamespace; vorbis_analysis_wrote (&vd, numSamples); while (vorbis_analysis_blockout (&vd, &vb) == 1) { vorbis_analysis (&vb, 0); vorbis_bitrate_addblock (&vb); while (vorbis_bitrate_flushpacket (&vd, &op)) { ogg_stream_packetin (&os, &op); for (;;) { if (ogg_stream_pageout (&os, &og) == 0) break; output->write (og.header, (size_t) og.header_len); output->write (og.body, (size_t) og.body_len); if (ogg_page_eos (&og)) break; } } } } bool ok; private: OggVorbisNamespace::ogg_stream_state os; OggVorbisNamespace::ogg_page og; OggVorbisNamespace::ogg_packet op; OggVorbisNamespace::vorbis_info vi; OggVorbisNamespace::vorbis_comment vc; OggVorbisNamespace::vorbis_dsp_state vd; OggVorbisNamespace::vorbis_block vb; void addMetadata (const StringPairArray& metadata, const char* name, const char* vorbisName) { const String s (metadata [name]); if (s.isNotEmpty()) vorbis_comment_add_tag (&vc, vorbisName, const_cast (s.toRawUTF8())); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter) }; //============================================================================== OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg") { } OggVorbisAudioFormat::~OggVorbisAudioFormat() { } Array OggVorbisAudioFormat::getPossibleSampleRates() { const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; return Array (rates, numElementsInArray (rates)); } Array OggVorbisAudioFormat::getPossibleBitDepths() { const int depths[] = { 32 }; return Array (depths, numElementsInArray (depths)); } bool OggVorbisAudioFormat::canDoStereo() { return true; } bool OggVorbisAudioFormat::canDoMono() { return true; } bool OggVorbisAudioFormat::isCompressed() { return true; } AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) { ScopedPointer r (new OggReader (in)); if (r->sampleRate > 0) return r.release(); if (! deleteStreamIfOpeningFails) r->input = nullptr; return nullptr; } AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out, double sampleRate, unsigned int numChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) { ScopedPointer w (new OggWriter (out, sampleRate, numChannels, (unsigned int) bitsPerSample, qualityOptionIndex, metadataValues)); return w->ok ? w.release() : nullptr; } StringArray OggVorbisAudioFormat::getQualityOptions() { static const char* options[] = { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps", "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps", 0 }; return StringArray (options); } int OggVorbisAudioFormat::estimateOggFileQuality (const File& source) { if (FileInputStream* const in = source.createInputStream()) { ScopedPointer r (createReaderFor (in, true)); if (r != nullptr) { const double lengthSecs = r->lengthInSamples / r->sampleRate; const int approxBitsPerSecond = (int) (source.getSize() * 8 / lengthSecs); const StringArray qualities (getQualityOptions()); int bestIndex = 0; int bestDiff = 10000; for (int i = qualities.size(); --i >= 0;) { const int diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond); if (diff < bestDiff) { bestDiff = diff; bestIndex = i; } } return bestIndex; } } return 0; } #endif juce_OggVorbisAudioFormat.h000066400000000000000000000077171320201440200340560ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_OGGVORBIS || defined (DOXYGEN) //============================================================================== /** Reads and writes the Ogg-Vorbis audio format. To compile this, you'll need to set the JUCE_USE_OGGVORBIS flag. @see AudioFormat, */ class JUCE_API OggVorbisAudioFormat : public AudioFormat { public: //============================================================================== OggVorbisAudioFormat(); ~OggVorbisAudioFormat(); //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; bool isCompressed() override; StringArray getQualityOptions() override; //============================================================================== /** Tries to estimate the quality level of an ogg file based on its size. If it can't read the file for some reason, this will just return 1 (medium quality), otherwise it will return the approximate quality setting that would have been used to create the file. @see getQualityOptions */ int estimateOggFileQuality (const File& source); //============================================================================== /** Metadata property name used by the Ogg writer - if you set a string for this value, it will be written into the ogg file as the name of the encoder app. @see createWriterFor */ static const char* const encoderName; static const char* const id3title; /**< Metadata key for setting an ID3 title. */ static const char* const id3artist; /**< Metadata key for setting an ID3 artist name. */ static const char* const id3album; /**< Metadata key for setting an ID3 album. */ static const char* const id3comment; /**< Metadata key for setting an ID3 comment. */ static const char* const id3date; /**< Metadata key for setting an ID3 date. */ static const char* const id3genre; /**< Metadata key for setting an ID3 genre. */ static const char* const id3trackNumber; /**< Metadata key for setting an ID3 track number. */ //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggVorbisAudioFormat) }; #endif juce_QuickTimeAudioFormat.cpp000066400000000000000000000346531320201440200344020ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_QUICKTIME && ! (JUCE_64BIT || JUCE_IOS) } // (juce namespace) #if ! JUCE_WINDOWS #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #define Component CarbonDummyCompName #include #include #include #include #include #undef Point #undef Component #else #if JUCE_MSVC #pragma warning (push) #pragma warning (disable : 4100) #endif /* If you've got an include error here, you probably need to install the QuickTime SDK and add its header directory to your include path. Alternatively, if you don't need any QuickTime services, just set the JUCE_QUICKTIME flag to 0. */ #undef SIZE_MAX #include #include #include #include #include #undef SIZE_MAX #if JUCE_MSVC #pragma warning (pop) #endif #endif namespace juce { bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); static const char* const quickTimeFormatName = "QuickTime file"; //============================================================================== class QTAudioReader : public AudioFormatReader { public: QTAudioReader (InputStream* const input_, const int trackNum_) : AudioFormatReader (input_, quickTimeFormatName), ok (false), movie (0), trackNum (trackNum_), lastSampleRead (0), lastThreadId (0), extractor (0), dataHandle (0) { JUCE_AUTORELEASEPOOL { bufferList.calloc (256, 1); #if JUCE_WINDOWS if (InitializeQTML (0) != noErr) return; #endif if (EnterMovies() != noErr) return; bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle); if (! opened) return; { const int numTracks = GetMovieTrackCount (movie); int trackCount = 0; for (int i = 1; i <= numTracks; ++i) { track = GetMovieIndTrack (movie, i); media = GetTrackMedia (track); OSType mediaType; GetMediaHandlerDescription (media, &mediaType, 0, 0); if (mediaType == SoundMediaType && trackCount++ == trackNum_) { ok = true; break; } } } if (! ok) return; ok = false; lengthInSamples = GetMediaDecodeDuration (media); usesFloatingPointData = false; samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media)); trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame / GetMediaTimeScale (media); MovieAudioExtractionBegin (movie, 0, &extractor); unsigned long output_layout_size; OSStatus err = MovieAudioExtractionGetPropertyInfo (extractor, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, 0, &output_layout_size, 0); if (err != noErr) return; HeapBlock qt_audio_channel_layout; qt_audio_channel_layout.calloc (output_layout_size, 1); MovieAudioExtractionGetProperty (extractor, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, output_layout_size, qt_audio_channel_layout, 0); qt_audio_channel_layout[0].mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; MovieAudioExtractionSetProperty (extractor, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, output_layout_size, qt_audio_channel_layout); err = MovieAudioExtractionGetProperty (extractor, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, sizeof (inputStreamDesc), &inputStreamDesc, 0); if (err != noErr) return; inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian; inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8; inputStreamDesc.mChannelsPerFrame = jmin ((UInt32) 2, inputStreamDesc.mChannelsPerFrame); inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame; inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame; err = MovieAudioExtractionSetProperty (extractor, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, sizeof (inputStreamDesc), &inputStreamDesc); if (err != noErr) return; Boolean allChannelsDiscrete = false; err = MovieAudioExtractionSetProperty (extractor, kQTPropertyClass_MovieAudioExtraction_Movie, kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, sizeof (allChannelsDiscrete), &allChannelsDiscrete); if (err != noErr) return; bufferList->mNumberBuffers = 1; bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * (int) inputStreamDesc.mBytesPerFrame) + 16); dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize); bufferList->mBuffers[0].mData = dataBuffer; sampleRate = inputStreamDesc.mSampleRate; bitsPerSample = 16; numChannels = inputStreamDesc.mChannelsPerFrame; detachThread(); ok = true; } } ~QTAudioReader() { JUCE_AUTORELEASEPOOL { checkThreadIsAttached(); if (dataHandle != nullptr) DisposeHandle (dataHandle); if (extractor != nullptr) { MovieAudioExtractionEnd (extractor); extractor = nullptr; } DisposeMovie (movie); #if JUCE_MAC ExitMoviesOnThread (); #endif } } bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { JUCE_AUTORELEASEPOOL { checkThreadIsAttached(); bool readOk = true; while (numSamples > 0) { if (lastSampleRead != startSampleInFile) { TimeRecord time; time.scale = (TimeScale) inputStreamDesc.mSampleRate; time.base = 0; time.value.hi = 0; time.value.lo = (UInt32) startSampleInFile; OSStatus err = MovieAudioExtractionSetProperty (extractor, kQTPropertyClass_MovieAudioExtraction_Movie, kQTMovieAudioExtractionMoviePropertyID_CurrentTime, sizeof (time), &time); if (err != noErr) { readOk = false; break; } } int framesToDo = jmin (numSamples, (int) (bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame)); bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * (UInt32) framesToDo; UInt32 outFlags = 0; UInt32 actualNumFrames = (UInt32) framesToDo; OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags); if (err != noErr) { readOk = false; break; } lastSampleRead = startSampleInFile + actualNumFrames; const int samplesReceived = (int) actualNumFrames; for (int j = numDestChannels; --j >= 0;) { if (destSamples[j] != nullptr) { const short* src = ((const short*) bufferList->mBuffers[0].mData) + j; for (int i = 0; i < samplesReceived; ++i) { destSamples[j][startOffsetInDestBuffer + i] = (*src << 16); src += numChannels; } } } startOffsetInDestBuffer += samplesReceived; startSampleInFile += samplesReceived; numSamples -= samplesReceived; if (((outFlags & kQTMovieAudioExtractionComplete) != 0 || samplesReceived == 0) && numSamples > 0) { for (int j = numDestChannels; --j >= 0;) if (destSamples[j] != nullptr) zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); break; } } detachThread(); return readOk; } } bool ok; private: Movie movie; Media media; Track track; const int trackNum; double trackUnitsPerFrame; int samplesPerFrame; int64 lastSampleRead; Thread::ThreadID lastThreadId; MovieAudioExtractionRef extractor; AudioStreamBasicDescription inputStreamDesc; HeapBlock bufferList; HeapBlock dataBuffer; Handle dataHandle; //============================================================================== void checkThreadIsAttached() { #if JUCE_MAC if (Thread::getCurrentThreadId() != lastThreadId) EnterMoviesOnThread (0); AttachMovieToCurrentThread (movie); #endif } void detachThread() { #if JUCE_MAC DetachMovieFromCurrentThread (movie); #endif } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QTAudioReader) }; //============================================================================== QuickTimeAudioFormat::QuickTimeAudioFormat() : AudioFormat (quickTimeFormatName, ".mov .mp3 .mp4 .m4a") { } QuickTimeAudioFormat::~QuickTimeAudioFormat() { } Array QuickTimeAudioFormat::getPossibleSampleRates() { return Array(); } Array QuickTimeAudioFormat::getPossibleBitDepths() { return Array(); } bool QuickTimeAudioFormat::canDoStereo() { return true; } bool QuickTimeAudioFormat::canDoMono() { return true; } //============================================================================== AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) { ScopedPointer r (new QTAudioReader (sourceStream, 0)); if (r->ok) return r.release(); if (! deleteStreamIfOpeningFails) r->input = 0; return nullptr; } AudioFormatWriter* QuickTimeAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/, double /*sampleRateToUse*/, unsigned int /*numberOfChannels*/, int /*bitsPerSample*/, const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/) { jassertfalse; // not yet implemented! return nullptr; } #endif juce_QuickTimeAudioFormat.h000066400000000000000000000047361320201440200340460ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_QUICKTIME //============================================================================== /** Uses QuickTime to read the audio track a movie or media file. As well as QuickTime movies, this should also manage to open other audio files that quicktime can understand, like mp3, m4a, etc. @see AudioFormat */ class JUCE_API QuickTimeAudioFormat : public AudioFormat { public: //============================================================================== /** Creates a format object. */ QuickTimeAudioFormat(); /** Destructor. */ ~QuickTimeAudioFormat(); //============================================================================== Array getPossibleSampleRates(); Array getPossibleBitDepths(); bool canDoStereo(); bool canDoMono(); //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails); AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex); private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QuickTimeAudioFormat) }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp000066400000000000000000002027131320201440200333150ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ static const char* const wavFormatName = "WAV file"; //============================================================================== const char* const WavAudioFormat::bwavDescription = "bwav description"; const char* const WavAudioFormat::bwavOriginator = "bwav originator"; const char* const WavAudioFormat::bwavOriginatorRef = "bwav originator ref"; const char* const WavAudioFormat::bwavOriginationDate = "bwav origination date"; const char* const WavAudioFormat::bwavOriginationTime = "bwav origination time"; const char* const WavAudioFormat::bwavTimeReference = "bwav time reference"; const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history"; StringPairArray WavAudioFormat::createBWAVMetadata (const String& description, const String& originator, const String& originatorRef, const Time date, const int64 timeReferenceSamples, const String& codingHistory) { StringPairArray m; m.set (bwavDescription, description); m.set (bwavOriginator, originator); m.set (bwavOriginatorRef, originatorRef); m.set (bwavOriginationDate, date.formatted ("%Y-%m-%d")); m.set (bwavOriginationTime, date.formatted ("%H:%M:%S")); m.set (bwavTimeReference, String (timeReferenceSamples)); m.set (bwavCodingHistory, codingHistory); return m; } const char* const WavAudioFormat::acidOneShot = "acid one shot"; const char* const WavAudioFormat::acidRootSet = "acid root set"; const char* const WavAudioFormat::acidStretch = "acid stretch"; const char* const WavAudioFormat::acidDiskBased = "acid disk based"; const char* const WavAudioFormat::acidizerFlag = "acidizer flag"; const char* const WavAudioFormat::acidRootNote = "acid root note"; const char* const WavAudioFormat::acidBeats = "acid beats"; const char* const WavAudioFormat::acidDenominator = "acid denominator"; const char* const WavAudioFormat::acidNumerator = "acid numerator"; const char* const WavAudioFormat::acidTempo = "acid tempo"; const char* const WavAudioFormat::ISRC = "ISRC"; const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; //============================================================================== namespace WavFileHelpers { inline int chunkName (const char* const name) noexcept { return (int) ByteOrder::littleEndianInt (name); } inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; } #if JUCE_MSVC #pragma pack (push, 1) #endif struct BWAVChunk { char description [256]; char originator [32]; char originatorRef [32]; char originationDate [10]; char originationTime [8]; uint32 timeRefLow; uint32 timeRefHigh; uint16 version; uint8 umid[64]; uint8 reserved[190]; char codingHistory[1]; void copyTo (StringPairArray& values, const int totalSize) const { values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, sizeof (description))); values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, sizeof (originator))); values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, sizeof (originatorRef))); values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, sizeof (originationDate))); values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, sizeof (originationTime))); const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow); const uint32 timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh); const int64 time = (((int64)timeHigh) << 32) + timeLow; values.set (WavAudioFormat::bwavTimeReference, String (time)); values.set (WavAudioFormat::bwavCodingHistory, String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory))); } static MemoryBlock createFrom (const StringPairArray& values) { MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + values [WavAudioFormat::bwavCodingHistory].getNumBytesAsUTF8())); data.fillWith (0); BWAVChunk* b = (BWAVChunk*) data.getData(); // Allow these calls to overwrite an extra byte at the end, which is fine as long // as they get called in the right order.. values [WavAudioFormat::bwavDescription] .copyToUTF8 (b->description, 257); values [WavAudioFormat::bwavOriginator] .copyToUTF8 (b->originator, 33); values [WavAudioFormat::bwavOriginatorRef] .copyToUTF8 (b->originatorRef, 33); values [WavAudioFormat::bwavOriginationDate].copyToUTF8 (b->originationDate, 11); values [WavAudioFormat::bwavOriginationTime].copyToUTF8 (b->originationTime, 9); const int64 time = values [WavAudioFormat::bwavTimeReference].getLargeIntValue(); b->timeRefLow = ByteOrder::swapIfBigEndian ((uint32) (time & 0xffffffff)); b->timeRefHigh = ByteOrder::swapIfBigEndian ((uint32) (time >> 32)); values [WavAudioFormat::bwavCodingHistory].copyToUTF8 (b->codingHistory, 0x7fffffff); if (b->description[0] != 0 || b->originator[0] != 0 || b->originationDate[0] != 0 || b->originationTime[0] != 0 || b->codingHistory[0] != 0 || time != 0) { return data; } return MemoryBlock(); } } JUCE_PACKED; //============================================================================== struct SMPLChunk { struct SampleLoop { uint32 identifier; uint32 type; // these are different in AIFF and WAV uint32 start; uint32 end; uint32 fraction; uint32 playCount; } JUCE_PACKED; uint32 manufacturer; uint32 product; uint32 samplePeriod; uint32 midiUnityNote; uint32 midiPitchFraction; uint32 smpteFormat; uint32 smpteOffset; uint32 numSampleLoops; uint32 samplerData; SampleLoop loops[1]; template static void setValue (StringPairArray& values, NameType name, uint32 val) { values.set (name, String (ByteOrder::swapIfBigEndian (val))); } static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) { setValue (values, "Loop" + String (prefix) + name, val); } void copyTo (StringPairArray& values, const int totalSize) const { setValue (values, "Manufacturer", manufacturer); setValue (values, "Product", product); setValue (values, "SamplePeriod", samplePeriod); setValue (values, "MidiUnityNote", midiUnityNote); setValue (values, "MidiPitchFraction", midiPitchFraction); setValue (values, "SmpteFormat", smpteFormat); setValue (values, "SmpteOffset", smpteOffset); setValue (values, "NumSampleLoops", numSampleLoops); setValue (values, "SamplerData", samplerData); for (int i = 0; i < (int) numSampleLoops; ++i) { if ((uint8*) (loops + (i + 1)) > ((uint8*) this) + totalSize) break; setValue (values, i, "Identifier", loops[i].identifier); setValue (values, i, "Type", loops[i].type); setValue (values, i, "Start", loops[i].start); setValue (values, i, "End", loops[i].end); setValue (values, i, "Fraction", loops[i].fraction); setValue (values, i, "PlayCount", loops[i].playCount); } } template static uint32 getValue (const StringPairArray& values, NameType name, const char* def) { return ByteOrder::swapIfBigEndian ((uint32) values.getValue (name, def).getIntValue()); } static uint32 getValue (const StringPairArray& values, int prefix, const char* name, const char* def) { return getValue (values, "Loop" + String (prefix) + name, def); } static MemoryBlock createFrom (const StringPairArray& values) { MemoryBlock data; const int numLoops = jmin (64, values.getValue ("NumSampleLoops", "0").getIntValue()); if (numLoops > 0) { data.setSize (roundUpSize (sizeof (SMPLChunk) + (size_t) (numLoops - 1) * sizeof (SampleLoop)), true); SMPLChunk* const s = static_cast (data.getData()); s->manufacturer = getValue (values, "Manufacturer", "0"); s->product = getValue (values, "Product", "0"); s->samplePeriod = getValue (values, "SamplePeriod", "0"); s->midiUnityNote = getValue (values, "MidiUnityNote", "60"); s->midiPitchFraction = getValue (values, "MidiPitchFraction", "0"); s->smpteFormat = getValue (values, "SmpteFormat", "0"); s->smpteOffset = getValue (values, "SmpteOffset", "0"); s->numSampleLoops = ByteOrder::swapIfBigEndian ((uint32) numLoops); s->samplerData = getValue (values, "SamplerData", "0"); for (int i = 0; i < numLoops; ++i) { SampleLoop& loop = s->loops[i]; loop.identifier = getValue (values, i, "Identifier", "0"); loop.type = getValue (values, i, "Type", "0"); loop.start = getValue (values, i, "Start", "0"); loop.end = getValue (values, i, "End", "0"); loop.fraction = getValue (values, i, "Fraction", "0"); loop.playCount = getValue (values, i, "PlayCount", "0"); } } return data; } } JUCE_PACKED; //============================================================================== struct InstChunk { int8 baseNote; int8 detune; int8 gain; int8 lowNote; int8 highNote; int8 lowVelocity; int8 highVelocity; static void setValue (StringPairArray& values, const char* name, int val) { values.set (name, String (val)); } void copyTo (StringPairArray& values) const { setValue (values, "MidiUnityNote", baseNote); setValue (values, "Detune", detune); setValue (values, "Gain", gain); setValue (values, "LowNote", lowNote); setValue (values, "HighNote", highNote); setValue (values, "LowVelocity", lowVelocity); setValue (values, "HighVelocity", highVelocity); } static int8 getValue (const StringPairArray& values, const char* name, const char* def) { return (int8) values.getValue (name, def).getIntValue(); } static MemoryBlock createFrom (const StringPairArray& values) { MemoryBlock data; const StringArray& keys = values.getAllKeys(); if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true)) { data.setSize (8, true); InstChunk* const inst = static_cast (data.getData()); inst->baseNote = getValue (values, "MidiUnityNote", "60"); inst->detune = getValue (values, "Detune", "0"); inst->gain = getValue (values, "Gain", "0"); inst->lowNote = getValue (values, "LowNote", "0"); inst->highNote = getValue (values, "HighNote", "127"); inst->lowVelocity = getValue (values, "LowVelocity", "1"); inst->highVelocity = getValue (values, "HighVelocity", "127"); } return data; } } JUCE_PACKED; //============================================================================== struct CueChunk { struct Cue { uint32 identifier; uint32 order; uint32 chunkID; uint32 chunkStart; uint32 blockStart; uint32 offset; } JUCE_PACKED; uint32 numCues; Cue cues[1]; static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) { values.set ("Cue" + String (prefix) + name, String (ByteOrder::swapIfBigEndian (val))); } void copyTo (StringPairArray& values, const int totalSize) const { values.set ("NumCuePoints", String (ByteOrder::swapIfBigEndian (numCues))); for (int i = 0; i < (int) numCues; ++i) { if ((uint8*) (cues + (i + 1)) > ((uint8*) this) + totalSize) break; setValue (values, i, "Identifier", cues[i].identifier); setValue (values, i, "Order", cues[i].order); setValue (values, i, "ChunkID", cues[i].chunkID); setValue (values, i, "ChunkStart", cues[i].chunkStart); setValue (values, i, "BlockStart", cues[i].blockStart); setValue (values, i, "Offset", cues[i].offset); } } static MemoryBlock createFrom (const StringPairArray& values) { MemoryBlock data; const int numCues = values.getValue ("NumCuePoints", "0").getIntValue(); if (numCues > 0) { data.setSize (roundUpSize (sizeof (CueChunk) + (size_t) (numCues - 1) * sizeof (Cue)), true); CueChunk* const c = static_cast (data.getData()); c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues); const String dataChunkID (chunkName ("data")); int nextOrder = 0; #if JUCE_DEBUG Array identifiers; #endif for (int i = 0; i < numCues; ++i) { const String prefix ("Cue" + String (i)); const uint32 identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue(); #if JUCE_DEBUG jassert (! identifiers.contains (identifier)); identifiers.add (identifier); #endif const int order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue(); nextOrder = jmax (nextOrder, order) + 1; Cue& cue = c->cues[i]; cue.identifier = ByteOrder::swapIfBigEndian ((uint32) identifier); cue.order = ByteOrder::swapIfBigEndian ((uint32) order); cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue()); cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue()); cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue()); cue.offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue()); } } return data; } } JUCE_PACKED; //============================================================================== namespace ListChunk { static int getValue (const StringPairArray& values, const String& name) { return values.getValue (name, "0").getIntValue(); } static int getValue (const StringPairArray& values, const String& prefix, const char* name) { return getValue (values, prefix + name); } static void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix, const int chunkType, MemoryOutputStream& out) { const String label (values.getValue (prefix + "Text", prefix)); const int labelLength = (int) label.getNumBytesAsUTF8() + 1; const int chunkLength = 4 + labelLength + (labelLength & 1); out.writeInt (chunkType); out.writeInt (chunkLength); out.writeInt (getValue (values, prefix, "Identifier")); out.write (label.toUTF8(), (size_t) labelLength); if ((out.getDataSize() & 1) != 0) out.writeByte (0); } static void appendExtraChunk (const StringPairArray& values, const String& prefix, MemoryOutputStream& out) { const String text (values.getValue (prefix + "Text", prefix)); const int textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator int chunkLength = textLength + 20 + (textLength & 1); out.writeInt (chunkName ("ltxt")); out.writeInt (chunkLength); out.writeInt (getValue (values, prefix, "Identifier")); out.writeInt (getValue (values, prefix, "SampleLength")); out.writeInt (getValue (values, prefix, "Purpose")); out.writeShort ((short) getValue (values, prefix, "Country")); out.writeShort ((short) getValue (values, prefix, "Language")); out.writeShort ((short) getValue (values, prefix, "Dialect")); out.writeShort ((short) getValue (values, prefix, "CodePage")); out.write (text.toUTF8(), (size_t) textLength); if ((out.getDataSize() & 1) != 0) out.writeByte (0); } static MemoryBlock createFrom (const StringPairArray& values) { const int numCueLabels = getValue (values, "NumCueLabels"); const int numCueNotes = getValue (values, "NumCueNotes"); const int numCueRegions = getValue (values, "NumCueRegions"); MemoryOutputStream out; if (numCueLabels + numCueNotes + numCueRegions > 0) { out.writeInt (chunkName ("adtl")); for (int i = 0; i < numCueLabels; ++i) appendLabelOrNoteChunk (values, "CueLabel" + String (i), chunkName ("labl"), out); for (int i = 0; i < numCueNotes; ++i) appendLabelOrNoteChunk (values, "CueNote" + String (i), chunkName ("note"), out); for (int i = 0; i < numCueRegions; ++i) appendExtraChunk (values, "CueRegion" + String (i), out); } return out.getMemoryBlock(); } } //============================================================================== namespace ListInfoChunk { static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName) { const String value (values.getValue (paramName, String())); if (value.isEmpty()) return false; const int valueLength = (int) value.getNumBytesAsUTF8() + 1; const int chunkLength = valueLength + (valueLength & 1); out.writeInt (chunkName (paramName)); out.writeInt (chunkLength); out.write (value.toUTF8(), (size_t) valueLength); if ((out.getDataSize() & 1) != 0) out.writeByte (0); return true; } static MemoryBlock createFrom (const StringPairArray& values) { static const char* params[] = { "INAM", "IART", "IPRD", "IPRT", "ISFT", "ISRC", "IGNR", "ICMT", "ICOP", "ICRD" }; MemoryOutputStream out; out.writeInt (chunkName ("INFO")); bool anyParamsDefined = false; for (int i = 0; i < numElementsInArray (params); ++i) if (writeValue (values, out, params[i])) anyParamsDefined = true; return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock(); } } //============================================================================== struct AcidChunk { /** Reads an acid RIFF chunk from a stream positioned just after the size byte. */ AcidChunk (InputStream& input, size_t length) { zerostruct (*this); input.read (this, (int) jmin (sizeof (*this), length)); } AcidChunk (const StringPairArray& values) { zerostruct (*this); flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01) | getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02) | getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04) | getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08) | getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10); if (values[WavAudioFormat::acidRootSet].getIntValue() != 0) rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue()); numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue()); meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue()); meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue()); if (values.containsKey (WavAudioFormat::acidTempo)) tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue()); } static MemoryBlock createFrom (const StringPairArray& values) { return AcidChunk (values).toMemoryBlock(); } MemoryBlock toMemoryBlock() const { return (flags != 0 || rootNote != 0 || numBeats != 0 || meterDenominator != 0 || meterNumerator != 0) ? MemoryBlock (this, sizeof (*this)) : MemoryBlock(); } void addToMetadata (StringPairArray& values) const { setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01); setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02); setBoolFlag (values, WavAudioFormat::acidStretch, 0x04); setBoolFlag (values, WavAudioFormat::acidDiskBased, 0x08); setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10); if (flags & 0x02) // root note set values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote))); values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats))); values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator))); values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator))); values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo))); } void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const { values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0"); } static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag) { return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0; } static float swapFloatByteOrder (const float x) noexcept { #ifdef JUCE_BIG_ENDIAN union { uint32 asInt; float asFloat; } n; n.asFloat = x; n.asInt = ByteOrder::swap (n.asInt); return n.asFloat; #else return x; #endif } uint32 flags; uint16 rootNote; uint16 reserved1; float reserved2; uint32 numBeats; uint16 meterDenominator; uint16 meterNumerator; float tempo; } JUCE_PACKED; //============================================================================== struct TracktionChunk { static MemoryBlock createFrom (const StringPairArray& values) { MemoryOutputStream out; const String s (values[WavAudioFormat::tracktionLoopInfo]); if (s.isNotEmpty()) { out.writeString (s); if ((out.getDataSize() & 1) != 0) out.writeByte (0); } return out.getMemoryBlock(); } }; //============================================================================== namespace AXMLChunk { static void addToMetadata (StringPairArray& destValues, const String& source) { ScopedPointer xml (XmlDocument::parse (source)); if (xml != nullptr && xml->hasTagName ("ebucore:ebuCoreMain")) { if (XmlElement* xml2 = xml->getChildByName ("ebucore:coreMetadata")) { if (XmlElement* xml3 = xml2->getChildByName ("ebucore:identifier")) { if (XmlElement* xml4 = xml3->getChildByName ("dc:identifier")) { const String ISRCCode (xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true)); if (ISRCCode.isNotEmpty()) destValues.set (WavAudioFormat::ISRC, ISRCCode); } } } } } static MemoryBlock createFrom (const StringPairArray& values) { const String ISRC (values.getValue (WavAudioFormat::ISRC, String::empty)); MemoryOutputStream xml; if (ISRC.isNotEmpty()) { xml << "" "" "" "ISRC:" << ISRC << "" "" "" ""; xml.writeRepeatedByte (0, xml.getDataSize()); // ensures even size, null termination and room for future growing } return xml.getMemoryBlock(); } }; //============================================================================== struct ExtensibleWavSubFormat { uint32 data1; uint16 data2; uint16 data3; uint8 data4[8]; bool operator== (const ExtensibleWavSubFormat& other) const noexcept { return memcmp (this, &other, sizeof (*this)) == 0; } bool operator!= (const ExtensibleWavSubFormat& other) const noexcept { return ! operator== (other); } } JUCE_PACKED; static const ExtensibleWavSubFormat pcmFormat = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; static const ExtensibleWavSubFormat IEEEFloatFormat = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; static const ExtensibleWavSubFormat ambisonicFormat = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } }; struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise { uint32 riffSizeLow; // low 4 byte size of RF64 block uint32 riffSizeHigh; // high 4 byte size of RF64 block uint32 dataSizeLow; // low 4 byte size of data chunk uint32 dataSizeHigh; // high 4 byte size of data chunk uint32 sampleCountLow; // low 4 byte sample count of fact chunk uint32 sampleCountHigh; // high 4 byte sample count of fact chunk uint32 tableLength; // number of valid entries in array 'table' } JUCE_PACKED; #if JUCE_MSVC #pragma pack (pop) #endif } //============================================================================== class WavAudioFormatReader : public AudioFormatReader { public: WavAudioFormatReader (InputStream* const in) : AudioFormatReader (in, wavFormatName), bwavChunkStart (0), bwavSize (0), dataLength (0), isRF64 (false) { using namespace WavFileHelpers; uint64 len = 0; uint64 end = 0; int cueNoteIndex = 0; int cueLabelIndex = 0; int cueRegionIndex = 0; const int firstChunkType = input->readInt(); if (firstChunkType == chunkName ("RF64")) { input->skipNextBytes (4); // size is -1 for RF64 isRF64 = true; } else if (firstChunkType == chunkName ("RIFF")) { len = (uint64) (uint32) input->readInt(); end = len + (uint64) input->getPosition(); } else { return; } const int64 startOfRIFFChunk = input->getPosition(); if (input->readInt() == chunkName ("WAVE")) { if (isRF64 && input->readInt() == chunkName ("ds64")) { const uint32 length = (uint32) input->readInt(); if (length < 28) return; const int64 chunkEnd = input->getPosition() + length + (length & 1); len = (uint64) input->readInt64(); end = len + (uint64) startOfRIFFChunk; dataLength = input->readInt64(); input->setPosition (chunkEnd); } while ((uint64) input->getPosition() < end && ! input->isExhausted()) { const int chunkType = input->readInt(); uint32 length = (uint32) input->readInt(); const int64 chunkEnd = input->getPosition() + length + (length & 1); if (chunkType == chunkName ("fmt ")) { // read the format chunk const unsigned short format = (unsigned short) input->readShort(); numChannels = (unsigned int) input->readShort(); sampleRate = input->readInt(); const int bytesPerSec = input->readInt(); input->skipNextBytes (2); bitsPerSample = (unsigned int) (int) input->readShort(); if (bitsPerSample > 64) { bytesPerFrame = bytesPerSec / (int) sampleRate; bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels; } else { bytesPerFrame = numChannels * bitsPerSample / 8; } if (format == 3) { usesFloatingPointData = true; } else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/) { if (length < 40) // too short { bytesPerFrame = 0; } else { input->skipNextBytes (4); // skip over size and bitsPerSample metadataValues.set ("ChannelMask", String (input->readInt())); ExtensibleWavSubFormat subFormat; subFormat.data1 = (uint32) input->readInt(); subFormat.data2 = (uint16) input->readShort(); subFormat.data3 = (uint16) input->readShort(); input->read (subFormat.data4, sizeof (subFormat.data4)); if (subFormat == IEEEFloatFormat) usesFloatingPointData = true; else if (subFormat != pcmFormat && subFormat != ambisonicFormat) bytesPerFrame = 0; } } else if (format != 1) { bytesPerFrame = 0; } } else if (chunkType == chunkName ("data")) { if (! isRF64) // data size is expected to be -1, actual data size is in ds64 chunk dataLength = length; dataChunkStart = input->getPosition(); lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; } else if (chunkType == chunkName ("bext")) { bwavChunkStart = input->getPosition(); bwavSize = length; HeapBlock bwav; bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); input->read (bwav, (int) length); bwav->copyTo (metadataValues, (int) length); } else if (chunkType == chunkName ("smpl")) { HeapBlock smpl; smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); input->read (smpl, (int) length); smpl->copyTo (metadataValues, (int) length); } else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which... { HeapBlock inst; inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); input->read (inst, (int) length); inst->copyTo (metadataValues); } else if (chunkType == chunkName ("cue ")) { HeapBlock cue; cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1); input->read (cue, (int) length); cue->copyTo (metadataValues, (int) length); } else if (chunkType == chunkName ("axml")) { MemoryBlock axml; input->readIntoMemoryBlock (axml, (ssize_t) length); AXMLChunk::addToMetadata (metadataValues, axml.toString()); } else if (chunkType == chunkName ("LIST")) { if (input->readInt() == chunkName ("adtl")) { while (input->getPosition() < chunkEnd) { const int adtlChunkType = input->readInt(); const uint32 adtlLength = (uint32) input->readInt(); const int64 adtlChunkEnd = input->getPosition() + (adtlLength + (adtlLength & 1)); if (adtlChunkType == chunkName ("labl") || adtlChunkType == chunkName ("note")) { String prefix; if (adtlChunkType == chunkName ("labl")) prefix << "CueLabel" << cueLabelIndex++; else if (adtlChunkType == chunkName ("note")) prefix << "CueNote" << cueNoteIndex++; const uint32 identifier = (uint32) input->readInt(); const int stringLength = (int) adtlLength - 4; MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, stringLength); metadataValues.set (prefix + "Identifier", String (identifier)); metadataValues.set (prefix + "Text", textBlock.toString()); } else if (adtlChunkType == chunkName ("ltxt")) { const String prefix ("CueRegion" + String (cueRegionIndex++)); const uint32 identifier = (uint32) input->readInt(); const uint32 sampleLength = (uint32) input->readInt(); const uint32 purpose = (uint32) input->readInt(); const uint16 country = (uint16) input->readInt(); const uint16 language = (uint16) input->readInt(); const uint16 dialect = (uint16) input->readInt(); const uint16 codePage = (uint16) input->readInt(); const uint32 stringLength = adtlLength - 20; MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, (int) stringLength); metadataValues.set (prefix + "Identifier", String (identifier)); metadataValues.set (prefix + "SampleLength", String (sampleLength)); metadataValues.set (prefix + "Purpose", String (purpose)); metadataValues.set (prefix + "Country", String (country)); metadataValues.set (prefix + "Language", String (language)); metadataValues.set (prefix + "Dialect", String (dialect)); metadataValues.set (prefix + "CodePage", String (codePage)); metadataValues.set (prefix + "Text", textBlock.toString()); } input->setPosition (adtlChunkEnd); } } } else if (chunkType == chunkName ("acid")) { AcidChunk (*input, length).addToMetadata (metadataValues); } else if (chunkType == chunkName ("Trkn")) { MemoryBlock tracktion; input->readIntoMemoryBlock (tracktion, (ssize_t) length); metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString()); } else if (chunkEnd <= input->getPosition()) { break; } input->setPosition (chunkEnd); } } if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex)); if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex)); if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex)); if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV"); } //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); if (numSamples <= 0) return true; input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); while (numSamples > 0) { const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) char tempBuffer [tempBufSize]; const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples); const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); if (bytesRead < numThisTime * bytesPerFrame) { jassert (bytesRead >= 0); zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead)); } copySampleData (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); startOffsetInDestBuffer += numThisTime; numSamples -= numThisTime; } return true; } static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData, int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels, const void* sourceData, int numChannels, int numSamples) noexcept { switch (bitsPerSample) { case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; case 32: if (usesFloatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; default: jassertfalse; break; } } int64 bwavChunkStart, bwavSize; int64 dataChunkStart, dataLength; int bytesPerFrame; bool isRF64; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader) }; //============================================================================== class WavAudioFormatWriter : public AudioFormatWriter { public: WavAudioFormatWriter (OutputStream* const out, const double rate, const unsigned int numChans, const unsigned int bits, const StringPairArray& metadataValues) : AudioFormatWriter (out, wavFormatName, rate, numChans, bits), lengthInSamples (0), bytesWritten (0), writeFailed (false) { using namespace WavFileHelpers; if (metadataValues.size() > 0) { // The meta data should have been santised for the WAV format. // If it was originally sourced from an AIFF file the MetaDataSource // key should be removed (or set to "WAV") once this has been done jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF"); bwavChunk = BWAVChunk::createFrom (metadataValues); axmlChunk = AXMLChunk::createFrom (metadataValues); smplChunk = SMPLChunk::createFrom (metadataValues); instChunk = InstChunk::createFrom (metadataValues); cueChunk = CueChunk ::createFrom (metadataValues); listChunk = ListChunk::createFrom (metadataValues); listInfoChunk = ListInfoChunk::createFrom (metadataValues); acidChunk = AcidChunk::createFrom (metadataValues); trckChunk = TracktionChunk::createFrom (metadataValues); } headerPosition = out->getPosition(); writeHeader(); } ~WavAudioFormatWriter() { writeHeader(); } //============================================================================== bool write (const int** data, int numSamples) override { jassert (numSamples >= 0); jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel! if (writeFailed) return false; const size_t bytes = numChannels * (unsigned int) numSamples * bitsPerSample / 8; tempBlock.ensureSize (bytes, false); switch (bitsPerSample) { case 8: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; case 16: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; case 24: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; case 32: WriteHelper::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; default: jassertfalse; break; } if (! output->write (tempBlock.getData(), bytes)) { // failed to write to disk, so let's try writing the header. // If it's just run out of disk space, then if it does manage // to write the header, we'll still have a useable file.. writeHeader(); writeFailed = true; return false; } bytesWritten += bytes; lengthInSamples += (uint64) numSamples; return true; } bool flush() override { const int64 lastWritePos = output->getPosition(); writeHeader(); if (output->setPosition (lastWritePos)) return true; // if this fails, you've given it an output stream that can't seek! It needs // to be able to seek back to write the header jassertfalse; return false; } private: MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk; uint64 lengthInSamples, bytesWritten; int64 headerPosition; bool writeFailed; static int getChannelMask (const int numChannels) noexcept { switch (numChannels) { case 1: return 0; case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT case 3: return 1 + 2 + 4; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER case 4: return 1 + 2 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT case 7: return 1 + 2 + 4 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT case 8: return 1 + 2 + 4 + 8 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT default: break; } return 0; } void writeHeader() { if ((bytesWritten & 1) != 0) // pad to an even length output->writeByte (0); using namespace WavFileHelpers; if (headerPosition != output->getPosition() && ! output->setPosition (headerPosition)) { // if this fails, you've given it an output stream that can't seek! It needs to be // able to seek back to go back and write the header after the data has been written. jassertfalse; return; } const size_t bytesPerFrame = numChannels * bitsPerSample / 8; uint64 audioDataSize = bytesPerFrame * lengthInSamples; const bool isRF64 = (bytesWritten >= 0x100000000LL); const bool isWaveFmtEx = isRF64 || (numChannels > 2); int64 riffChunkSize = (int64) (4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */ + 8 + audioDataSize + (audioDataSize & 1) + chunkSize (bwavChunk) + chunkSize (axmlChunk) + chunkSize (smplChunk) + chunkSize (instChunk) + chunkSize (cueChunk) + chunkSize (listChunk) + chunkSize (listInfoChunk) + chunkSize (acidChunk) + chunkSize (trckChunk) + (8 + 28)); // (ds64 chunk) riffChunkSize += (riffChunkSize & 1); if (isRF64) writeChunkHeader (chunkName ("RF64"), -1); else writeChunkHeader (chunkName ("RIFF"), (int) riffChunkSize); output->writeInt (chunkName ("WAVE")); if (! isRF64) { #if ! JUCE_WAV_DO_NOT_PAD_HEADER_SIZE /* NB: This junk chunk is added for padding, so that the header is a fixed size regardless of whether it's RF64 or not. That way, we can begin recording a file, and when it's finished, can go back and write either a RIFF or RF64 header, depending on whether more than 2^32 samples were written. The JUCE_WAV_DO_NOT_PAD_HEADER_SIZE macro allows you to disable this feature in case you need to create files for crappy WAV players with bugs that stop them skipping chunks which they don't recognise. But DO NOT USE THIS option unless you really have no choice, because it means that if you write more than 2^32 samples to the file, you'll corrupt it. */ writeChunkHeader (chunkName ("JUNK"), 28 + (isWaveFmtEx? 0 : 24)); output->writeRepeatedByte (0, 28 /* ds64 */ + (isWaveFmtEx? 0 : 24)); #endif } else { #if JUCE_WAV_DO_NOT_PAD_HEADER_SIZE // If you disable padding, then you MUST NOT write more than 2^32 samples to a file. jassertfalse; #endif writeChunkHeader (chunkName ("ds64"), 28); // chunk size for uncompressed data (no table) output->writeInt64 (riffChunkSize); output->writeInt64 ((int64) audioDataSize); output->writeRepeatedByte (0, 12); } if (isWaveFmtEx) { writeChunkHeader (chunkName ("fmt "), 40); output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE } else { writeChunkHeader (chunkName ("fmt "), 16); output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/ : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); } output->writeShort ((short) numChannels); output->writeInt ((int) sampleRate); output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec output->writeShort ((short) bytesPerFrame); // nBlockAlign output->writeShort ((short) bitsPerSample); // wBitsPerSample if (isWaveFmtEx) { output->writeShort (22); // cbSize (size of the extension) output->writeShort ((short) bitsPerSample); // wValidBitsPerSample output->writeInt (getChannelMask ((int) numChannels)); const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; output->writeInt ((int) subFormat.data1); output->writeShort ((short) subFormat.data2); output->writeShort ((short) subFormat.data3); output->write (subFormat.data4, sizeof (subFormat.data4)); } writeChunk (bwavChunk, chunkName ("bext")); writeChunk (axmlChunk, chunkName ("axml")); writeChunk (smplChunk, chunkName ("smpl")); writeChunk (instChunk, chunkName ("inst"), 7); writeChunk (cueChunk, chunkName ("cue ")); writeChunk (listChunk, chunkName ("LIST")); writeChunk (listInfoChunk, chunkName ("LIST")); writeChunk (acidChunk, chunkName ("acid")); writeChunk (trckChunk, chunkName ("Trkn")); writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); usesFloatingPointData = (bitsPerSample == 32); } static size_t chunkSize (const MemoryBlock& data) noexcept { return data.getSize() > 0 ? (8 + data.getSize()) : 0; } void writeChunkHeader (int chunkType, int size) const { output->writeInt (chunkType); output->writeInt (size); } void writeChunk (const MemoryBlock& data, int chunkType, int size = 0) const { if (data.getSize() > 0) { writeChunkHeader (chunkType, size != 0 ? size : (int) data.getSize()); *output << data; } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter) }; //============================================================================== class MemoryMappedWavReader : public MemoryMappedAudioFormatReader { public: MemoryMappedWavReader (const File& wavFile, const WavAudioFormatReader& reader) : MemoryMappedAudioFormatReader (wavFile, reader, reader.dataChunkStart, reader.dataLength, reader.bytesPerFrame) { } bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. return false; } WavAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples); return true; } void getSample (int64 sample, float* result) const noexcept override { const int num = (int) numChannels; if (map == nullptr || ! mappedSection.contains (sample)) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. zeromem (result, sizeof (float) * (size_t) num); return; } float** dest = &result; const void* source = sampleToPointer (sample); switch (bitsPerSample) { case 8: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 16: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 24: ReadHelper::read (dest, 0, 1, source, 1, num); break; case 32: if (usesFloatingPointData) ReadHelper::read (dest, 0, 1, source, 1, num); else ReadHelper::read (dest, 0, 1, source, 1, num); break; default: jassertfalse; break; } } void readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) override { if (numSamples <= 0) { for (int i = 0; i < numChannelsToRead; ++i) results[i] = Range(); return; } if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. for (int i = 0; i < numChannelsToRead; ++i) results[i] = Range(); return; } switch (bitsPerSample) { case 8: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; case 16: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; case 24: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); else scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; default: jassertfalse; break; } } private: template void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) const noexcept { for (int i = 0; i < numChannelsToRead; ++i) results[i] = scanMinAndMaxInterleaved (i, startSampleInFile, numSamples); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader) }; //============================================================================== WavAudioFormat::WavAudioFormat() : AudioFormat (wavFormatName, ".wav .bwf") {} WavAudioFormat::~WavAudioFormat() {} Array WavAudioFormat::getPossibleSampleRates() { const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; return Array (rates, numElementsInArray (rates)); } Array WavAudioFormat::getPossibleBitDepths() { const int depths[] = { 8, 16, 24, 32 }; return Array (depths, numElementsInArray (depths)); } bool WavAudioFormat::canDoStereo() { return true; } bool WavAudioFormat::canDoMono() { return true; } AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) { ScopedPointer r (new WavAudioFormatReader (sourceStream)); if (r->sampleRate > 0 && r->numChannels > 0 && r->bytesPerFrame > 0) return r.release(); if (! deleteStreamIfOpeningFails) r->input = nullptr; return nullptr; } MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (const File& file) { if (FileInputStream* fin = file.createInputStream()) { WavAudioFormatReader reader (fin); if (reader.lengthInSamples > 0) return new MemoryMappedWavReader (file, reader); } return nullptr; } AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate, unsigned int numChannels, int bitsPerSample, const StringPairArray& metadataValues, int /*qualityOptionIndex*/) { if (getPossibleBitDepths().contains (bitsPerSample)) return new WavAudioFormatWriter (out, sampleRate, (unsigned int) numChannels, (unsigned int) bitsPerSample, metadataValues); return nullptr; } namespace WavFileHelpers { static bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata) { TemporaryFile tempFile (file); WavAudioFormat wav; ScopedPointer reader (wav.createReaderFor (file.createInputStream(), true)); if (reader != nullptr) { ScopedPointer outStream (tempFile.getFile().createOutputStream()); if (outStream != nullptr) { ScopedPointer writer (wav.createWriterFor (outStream, reader->sampleRate, reader->numChannels, (int) reader->bitsPerSample, metadata, 0)); if (writer != nullptr) { outStream.release(); bool ok = writer->writeFromAudioReader (*reader, 0, -1); writer = nullptr; reader = nullptr; return ok && tempFile.overwriteTargetFileWithTemporary(); } } } return false; } } bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata) { using namespace WavFileHelpers; ScopedPointer reader (static_cast (createReaderFor (wavFile.createInputStream(), true))); if (reader != nullptr) { const int64 bwavPos = reader->bwavChunkStart; const int64 bwavSize = reader->bwavSize; reader = nullptr; if (bwavSize > 0) { MemoryBlock chunk (BWAVChunk::createFrom (newMetadata)); if (chunk.getSize() <= (size_t) bwavSize) { // the new one will fit in the space available, so write it directly.. const int64 oldSize = wavFile.getSize(); { FileOutputStream out (wavFile); if (! out.failedToOpen()) { out.setPosition (bwavPos); out << chunk; out.setPosition (oldSize); } } jassert (wavFile.getSize() == oldSize); return true; } } } return slowCopyWavFileWithNewMetadata (wavFile, newMetadata); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h000066400000000000000000000162321320201440200327610ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ //============================================================================== /** Reads and Writes WAV format audio files. @see AudioFormat */ class JUCE_API WavAudioFormat : public AudioFormat { public: //============================================================================== /** Creates a format object. */ WavAudioFormat(); /** Destructor. */ ~WavAudioFormat(); //============================================================================== /** Metadata property name used by wav readers and writers for adding a BWAV chunk to the file. @see AudioFormatReader::metadataValues, createWriterFor */ static const char* const bwavDescription; /** Metadata property name used by wav readers and writers for adding a BWAV chunk to the file. @see AudioFormatReader::metadataValues, createWriterFor */ static const char* const bwavOriginator; /** Metadata property name used by wav readers and writers for adding a BWAV chunk to the file. @see AudioFormatReader::metadataValues, createWriterFor */ static const char* const bwavOriginatorRef; /** Metadata property name used by wav readers and writers for adding a BWAV chunk to the file. Date format is: yyyy-mm-dd @see AudioFormatReader::metadataValues, createWriterFor */ static const char* const bwavOriginationDate; /** Metadata property name used by wav readers and writers for adding a BWAV chunk to the file. Time format is: hh-mm-ss @see AudioFormatReader::metadataValues, createWriterFor */ static const char* const bwavOriginationTime; /** Metadata property name used by wav readers and writers for adding a BWAV chunk to the file. This is the number of samples from the start of an edit that the file is supposed to begin at. Seems like an obvious mistake to only allow a file to occur in an edit once, but that's the way it is.. @see AudioFormatReader::metadataValues, createWriterFor */ static const char* const bwavTimeReference; /** Metadata property name used by wav readers and writers for adding a BWAV chunk to the file. @see AudioFormatReader::metadataValues, createWriterFor */ static const char* const bwavCodingHistory; /** Utility function to fill out the appropriate metadata for a BWAV file. This just makes it easier than using the property names directly, and it fills out the time and date in the right format. */ static StringPairArray createBWAVMetadata (const String& description, const String& originator, const String& originatorRef, const Time dateAndTime, const int64 timeReferenceSamples, const String& codingHistory); //============================================================================== /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidOneShot; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidRootSet; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidStretch; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidDiskBased; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidizerFlag; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidRootNote; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidBeats; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidDenominator; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidNumerator; /** Metadata property name used when reading a WAV file with an acid chunk. */ static const char* const acidTempo; //============================================================================== /** Metadata property name used when reading an ISRC code from an AXML chunk. */ static const char* const ISRC; /** Metadata property name used when reading a WAV file with a Tracktion chunk. */ static const char* const tracktionLoopInfo; //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) override; MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; //============================================================================== /** Utility function to replace the metadata in a wav file with a new set of values. If possible, this cheats by overwriting just the metadata region of the file, rather than by copying the whole file again. */ bool replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata); private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormat) }; juce_WindowsMediaAudioFormat.cpp000066400000000000000000000311051320201440200350660ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace WindowsMediaCodec { class JuceIStream : public ComBaseClassHelper { public: JuceIStream (InputStream& in) noexcept : ComBaseClassHelper (0), source (in) { } JUCE_COMRESULT Commit (DWORD) { return S_OK; } JUCE_COMRESULT Write (const void*, ULONG, ULONG*) { return E_NOTIMPL; } JUCE_COMRESULT Clone (IStream**) { return E_NOTIMPL; } JUCE_COMRESULT SetSize (ULARGE_INTEGER) { return E_NOTIMPL; } JUCE_COMRESULT Revert() { return E_NOTIMPL; } JUCE_COMRESULT LockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; } JUCE_COMRESULT UnlockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; } JUCE_COMRESULT Read (void* dest, ULONG numBytes, ULONG* bytesRead) { const int numRead = source.read (dest, numBytes); if (bytesRead != nullptr) *bytesRead = numRead; return (numRead == (int) numBytes) ? S_OK : S_FALSE; } JUCE_COMRESULT Seek (LARGE_INTEGER position, DWORD origin, ULARGE_INTEGER* resultPosition) { int64 newPos = (int64) position.QuadPart; if (origin == STREAM_SEEK_CUR) { newPos += source.getPosition(); } else if (origin == STREAM_SEEK_END) { const int64 len = source.getTotalLength(); if (len < 0) return E_NOTIMPL; newPos += len; } if (resultPosition != nullptr) resultPosition->QuadPart = newPos; return source.setPosition (newPos) ? S_OK : E_NOTIMPL; } JUCE_COMRESULT CopyTo (IStream* destStream, ULARGE_INTEGER numBytesToDo, ULARGE_INTEGER* bytesRead, ULARGE_INTEGER* bytesWritten) { uint64 totalCopied = 0; int64 numBytes = numBytesToDo.QuadPart; while (numBytes > 0 && ! source.isExhausted()) { char buffer [1024]; const int numToCopy = (int) jmin ((int64) sizeof (buffer), (int64) numBytes); const int numRead = source.read (buffer, numToCopy); if (numRead <= 0) break; destStream->Write (buffer, numRead, nullptr); totalCopied += numRead; } if (bytesRead != nullptr) bytesRead->QuadPart = totalCopied; if (bytesWritten != nullptr) bytesWritten->QuadPart = totalCopied; return S_OK; } JUCE_COMRESULT Stat (STATSTG* stat, DWORD) { if (stat == nullptr) return STG_E_INVALIDPOINTER; zerostruct (*stat); stat->type = STGTY_STREAM; stat->cbSize.QuadPart = jmax ((int64) 0, source.getTotalLength()); return S_OK; } private: InputStream& source; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceIStream) }; //============================================================================== static const char* wmFormatName = "Windows Media"; static const char* const extensions[] = { ".mp3", ".wmv", ".asf", ".wm", ".wma", 0 }; //============================================================================== class WMAudioReader : public AudioFormatReader { public: WMAudioReader (InputStream* const input_) : AudioFormatReader (input_, TRANS (wmFormatName)), wmvCoreLib ("Wmvcore.dll") { JUCE_LOAD_WINAPI_FUNCTION (wmvCoreLib, WMCreateSyncReader, wmCreateSyncReader, HRESULT, (IUnknown*, DWORD, IWMSyncReader**)) if (wmCreateSyncReader != nullptr) { checkCoInitialiseCalled(); HRESULT hr = wmCreateSyncReader (nullptr, WMT_RIGHT_PLAYBACK, wmSyncReader.resetAndGetPointerAddress()); if (SUCCEEDED (hr)) hr = wmSyncReader->OpenStream (new JuceIStream (*input)); if (SUCCEEDED (hr)) { WORD streamNum = 1; hr = wmSyncReader->GetStreamNumberForOutput (0, &streamNum); hr = wmSyncReader->SetReadStreamSamples (streamNum, false); scanFileForDetails(); } } } ~WMAudioReader() { if (wmSyncReader != nullptr) wmSyncReader->Close(); } bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { if (sampleRate <= 0) return false; checkCoInitialiseCalled(); const int stride = numChannels * sizeof (int16); while (numSamples > 0) { if (! bufferedRange.contains (startSampleInFile)) { const bool hasJumped = (startSampleInFile != bufferedRange.getEnd()); if (hasJumped) wmSyncReader->SetRange ((QWORD) (startSampleInFile * 10000000 / (int64) sampleRate), 0); ComSmartPtr sampleBuffer; QWORD sampleTime, duration; DWORD flags, outputNum; WORD streamNum; HRESULT hr = wmSyncReader->GetNextSample (1, sampleBuffer.resetAndGetPointerAddress(), &sampleTime, &duration, &flags, &outputNum, &streamNum); if (sampleBuffer != nullptr) { BYTE* rawData = nullptr; DWORD dataLength = 0; hr = sampleBuffer->GetBufferAndLength (&rawData, &dataLength); if (dataLength == 0) return false; if (hasJumped) bufferedRange.setStart ((int64) ((sampleTime * (int64) sampleRate) / 10000000)); else bufferedRange.setStart (bufferedRange.getEnd()); // (because the positions returned often aren't continguous) bufferedRange.setLength ((int64) (dataLength / stride)); buffer.ensureSize ((int) dataLength); memcpy (buffer.getData(), rawData, (size_t) dataLength); } else if (hr == NS_E_NO_MORE_SAMPLES) { bufferedRange.setStart (startSampleInFile); bufferedRange.setLength (256); buffer.ensureSize (256 * stride); buffer.fillWith (0); } else { return false; } } const int offsetInBuffer = (int) (startSampleInFile - bufferedRange.getStart()); const int16* const rawData = static_cast (addBytesToPointer (buffer.getData(), offsetInBuffer * stride)); const int numToDo = jmin (numSamples, (int) (bufferedRange.getLength() - offsetInBuffer)); for (int i = 0; i < numDestChannels; ++i) { jassert (destSamples[i] != nullptr); const int srcChan = jmin (i, (int) numChannels - 1); const int16* src = rawData + srcChan; int* const dst = destSamples[i] + startOffsetInDestBuffer; for (int j = 0; j < numToDo; ++j) { dst[j] = ((uint32) *src) << 16; src += numChannels; } } startSampleInFile += numToDo; startOffsetInDestBuffer += numToDo; numSamples -= numToDo; } return true; } private: DynamicLibrary wmvCoreLib; ComSmartPtr wmSyncReader; MemoryBlock buffer; Range bufferedRange; void checkCoInitialiseCalled() { CoInitialize (0); } void scanFileForDetails() { ComSmartPtr wmHeaderInfo; HRESULT hr = wmSyncReader.QueryInterface (wmHeaderInfo); if (SUCCEEDED (hr)) { QWORD lengthInNanoseconds = 0; WORD lengthOfLength = sizeof (lengthInNanoseconds); WORD streamNum = 0; WMT_ATTR_DATATYPE wmAttrDataType; hr = wmHeaderInfo->GetAttributeByName (&streamNum, L"Duration", &wmAttrDataType, (BYTE*) &lengthInNanoseconds, &lengthOfLength); ComSmartPtr wmProfile; hr = wmSyncReader.QueryInterface (wmProfile); if (SUCCEEDED (hr)) { ComSmartPtr wmStreamConfig; hr = wmProfile->GetStream (0, wmStreamConfig.resetAndGetPointerAddress()); if (SUCCEEDED (hr)) { ComSmartPtr wmMediaProperties; hr = wmStreamConfig.QueryInterface (wmMediaProperties); if (SUCCEEDED (hr)) { DWORD sizeMediaType; hr = wmMediaProperties->GetMediaType (0, &sizeMediaType); HeapBlock mediaType; mediaType.malloc (sizeMediaType, 1); hr = wmMediaProperties->GetMediaType (mediaType, &sizeMediaType); if (mediaType->majortype == WMMEDIATYPE_Audio) { const WAVEFORMATEX* const inputFormat = reinterpret_cast (mediaType->pbFormat); sampleRate = inputFormat->nSamplesPerSec; numChannels = inputFormat->nChannels; bitsPerSample = inputFormat->wBitsPerSample; lengthInSamples = (lengthInNanoseconds * (int) sampleRate) / 10000000; } } } } } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WMAudioReader) }; } //============================================================================== WindowsMediaAudioFormat::WindowsMediaAudioFormat() : AudioFormat (TRANS (WindowsMediaCodec::wmFormatName), StringArray (WindowsMediaCodec::extensions)) { } WindowsMediaAudioFormat::~WindowsMediaAudioFormat() {} Array WindowsMediaAudioFormat::getPossibleSampleRates() { return Array(); } Array WindowsMediaAudioFormat::getPossibleBitDepths() { return Array(); } bool WindowsMediaAudioFormat::canDoStereo() { return true; } bool WindowsMediaAudioFormat::canDoMono() { return true; } bool WindowsMediaAudioFormat::isCompressed() { return true; } //============================================================================== AudioFormatReader* WindowsMediaAudioFormat::createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) { ScopedPointer r (new WindowsMediaCodec::WMAudioReader (sourceStream)); if (r->sampleRate > 0) return r.release(); if (! deleteStreamIfOpeningFails) r->input = nullptr; return nullptr; } AudioFormatWriter* WindowsMediaAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/, double /*sampleRateToUse*/, unsigned int /*numberOfChannels*/, int /*bitsPerSample*/, const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/) { jassertfalse; // not yet implemented! return nullptr; } juce_WindowsMediaAudioFormat.h000066400000000000000000000041031320201440200345310ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/codecs/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_WINDOWS || DOXYGEN //============================================================================== /** Audio format which uses the Windows Media codecs (Windows only). */ class WindowsMediaAudioFormat : public AudioFormat { public: //============================================================================== WindowsMediaAudioFormat(); ~WindowsMediaAudioFormat(); //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; bool isCompressed() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/000077500000000000000000000000001320201440200267365ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp000066400000000000000000000036161320201440200326700ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioFormat::AudioFormat (String name, StringArray extensions) : formatName (name), fileExtensions (extensions) { } AudioFormat::AudioFormat (StringRef name, StringRef extensions) : formatName (name.text), fileExtensions (StringArray::fromTokens (extensions, false)) { } AudioFormat::~AudioFormat() { } bool AudioFormat::canHandleFile (const File& f) { for (int i = 0; i < fileExtensions.size(); ++i) if (f.hasFileExtension (fileExtensions[i])) return true; return false; } const String& AudioFormat::getFormatName() const { return formatName; } const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; } bool AudioFormat::isCompressed() { return false; } StringArray AudioFormat::getQualityOptions() { return StringArray(); } MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (const File&) { return nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h000066400000000000000000000207321320201440200323330ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOFORMAT_H_INCLUDED #define JUCE_AUDIOFORMAT_H_INCLUDED //============================================================================== /** Subclasses of AudioFormat are used to read and write different audio file formats. @see AudioFormatReader, AudioFormatWriter, WavAudioFormat, AiffAudioFormat */ class JUCE_API AudioFormat { public: //============================================================================== /** Destructor. */ virtual ~AudioFormat(); //============================================================================== /** Returns the name of this format. e.g. "WAV file" or "AIFF file" */ const String& getFormatName() const; /** Returns all the file extensions that might apply to a file of this format. The first item will be the one that's preferred when creating a new file. So for a wav file this might just return ".wav"; for an AIFF file it might return two items, ".aif" and ".aiff" */ const StringArray& getFileExtensions() const; //============================================================================== /** Returns true if this the given file can be read by this format. Subclasses shouldn't do too much work here, just check the extension or file type. The base class implementation just checks the file's extension against one of the ones that was registered in the constructor. */ virtual bool canHandleFile (const File& fileToTest); /** Returns a set of sample rates that the format can read and write. */ virtual Array getPossibleSampleRates() = 0; /** Returns a set of bit depths that the format can read and write. */ virtual Array getPossibleBitDepths() = 0; /** Returns true if the format can do 2-channel audio. */ virtual bool canDoStereo() = 0; /** Returns true if the format can do 1-channel audio. */ virtual bool canDoMono() = 0; /** Returns true if the format uses compressed data. */ virtual bool isCompressed(); /** Returns a list of different qualities that can be used when writing. Non-compressed formats will just return an empty array, but for something like Ogg-Vorbis or MP3, it might return a list of bit-rates, etc. When calling createWriterFor(), an index from this array is passed in to tell the format which option is required. */ virtual StringArray getQualityOptions(); //============================================================================== /** Tries to create an object that can read from a stream containing audio data in this format. The reader object that is returned can be used to read from the stream, and should then be deleted by the caller. @param sourceStream the stream to read from - the AudioFormatReader object that is returned will delete this stream when it no longer needs it. @param deleteStreamIfOpeningFails if no reader can be created, this determines whether this method should delete the stream object that was passed-in. (If a valid reader is returned, it will always be in charge of deleting the stream, so this parameter is ignored) @see AudioFormatReader */ virtual AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) = 0; /** Attempts to create a MemoryMappedAudioFormatReader, if possible for this format. If the format does not support this, the method will return nullptr; */ virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file); /** Tries to create an object that can write to a stream with this audio format. The writer object that is returned can be used to write to the stream, and should then be deleted by the caller. If the stream can't be created for some reason (e.g. the parameters passed in here aren't suitable), this will return nullptr. @param streamToWriteTo the stream that the data will go to - this will be deleted by the AudioFormatWriter object when it's no longer needed. If no AudioFormatWriter can be created by this method, the stream will NOT be deleted, so that the caller can re-use it to try to open a different format, etc @param sampleRateToUse the sample rate for the file, which must be one of the ones returned by getPossibleSampleRates() @param numberOfChannels the number of channels - this must be either 1 or 2, and the choice will depend on the results of canDoMono() and canDoStereo() @param bitsPerSample the bits per sample to use - this must be one of the values returned by getPossibleBitDepths() @param metadataValues a set of metadata values that the writer should try to write to the stream. Exactly what these are depends on the format, and the subclass doesn't actually have to do anything with them if it doesn't want to. Have a look at the specific format implementation classes to see possible values that can be used @param qualityOptionIndex the index of one of compression qualities returned by the getQualityOptions() method. If there aren't any quality options for this format, just pass 0 in this parameter, as it'll be ignored @see AudioFormatWriter */ virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) = 0; protected: /** Creates an AudioFormat object. @param formatName this sets the value that will be returned by getFormatName() @param fileExtensions an array of file extensions - these will be returned by getFileExtensions() */ AudioFormat (String formatName, StringArray fileExtensions); /** Creates an AudioFormat object. @param formatName this sets the value that will be returned by getFormatName() @param fileExtensions a whitespace-separated list of file extensions - these will be returned by getFileExtensions() */ AudioFormat (StringRef formatName, StringRef fileExtensions); private: //============================================================================== String formatName; StringArray fileExtensions; }; #endif // JUCE_AUDIOFORMAT_H_INCLUDED juce_AudioFormatManager.cpp000066400000000000000000000124371320201440200341050ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioFormatManager::AudioFormatManager() : defaultFormatIndex (0) {} AudioFormatManager::~AudioFormatManager() {} //============================================================================== void AudioFormatManager::registerFormat (AudioFormat* newFormat, const bool makeThisTheDefaultFormat) { jassert (newFormat != nullptr); if (newFormat != nullptr) { #if JUCE_DEBUG for (int i = getNumKnownFormats(); --i >= 0;) { if (getKnownFormat (i)->getFormatName() == newFormat->getFormatName()) { jassertfalse; // trying to add the same format twice! } } #endif if (makeThisTheDefaultFormat) defaultFormatIndex = getNumKnownFormats(); knownFormats.add (newFormat); } } void AudioFormatManager::registerBasicFormats() { registerFormat (new WavAudioFormat(), true); registerFormat (new AiffAudioFormat(), false); #if JUCE_USE_FLAC registerFormat (new FlacAudioFormat(), false); #endif #if JUCE_USE_OGGVORBIS registerFormat (new OggVorbisAudioFormat(), false); #endif #if JUCE_MAC || JUCE_IOS registerFormat (new CoreAudioFormat(), false); #endif #if JUCE_USE_MP3AUDIOFORMAT registerFormat (new MP3AudioFormat(), false); #endif #if JUCE_USE_WINDOWS_MEDIA_FORMAT registerFormat (new WindowsMediaAudioFormat(), false); #endif } void AudioFormatManager::clearFormats() { knownFormats.clear(); defaultFormatIndex = 0; } int AudioFormatManager::getNumKnownFormats() const { return knownFormats.size(); } AudioFormat* AudioFormatManager::getKnownFormat (const int index) const { return knownFormats [index]; } AudioFormat* AudioFormatManager::getDefaultFormat() const { return getKnownFormat (defaultFormatIndex); } AudioFormat* AudioFormatManager::findFormatForFileExtension (const String& fileExtension) const { if (! fileExtension.startsWithChar ('.')) return findFormatForFileExtension ("." + fileExtension); for (int i = 0; i < getNumKnownFormats(); ++i) if (getKnownFormat(i)->getFileExtensions().contains (fileExtension, true)) return getKnownFormat(i); return nullptr; } String AudioFormatManager::getWildcardForAllFormats() const { StringArray extensions; for (int i = 0; i < getNumKnownFormats(); ++i) extensions.addArray (getKnownFormat(i)->getFileExtensions()); extensions.trim(); extensions.removeEmptyStrings(); for (int i = 0; i < extensions.size(); ++i) extensions.set (i, (extensions[i].startsWithChar ('.') ? "*" : "*.") + extensions[i]); extensions.removeDuplicates (true); return extensions.joinIntoString (";"); } //============================================================================== AudioFormatReader* AudioFormatManager::createReaderFor (const File& file) { // you need to actually register some formats before the manager can // use them to open a file! jassert (getNumKnownFormats() > 0); for (int i = 0; i < getNumKnownFormats(); ++i) { AudioFormat* const af = getKnownFormat(i); if (af->canHandleFile (file)) if (InputStream* const in = file.createInputStream()) if (AudioFormatReader* const r = af->createReaderFor (in, true)) return r; } return nullptr; } AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* audioFileStream) { // you need to actually register some formats before the manager can // use them to open a file! jassert (getNumKnownFormats() > 0); ScopedPointer in (audioFileStream); if (in != nullptr) { const int64 originalStreamPos = in->getPosition(); for (int i = 0; i < getNumKnownFormats(); ++i) { if (AudioFormatReader* const r = getKnownFormat(i)->createReaderFor (in, false)) { in.release(); return r; } in->setPosition (originalStreamPos); // the stream that is passed-in must be capable of being repositioned so // that all the formats can have a go at opening it. jassert (in->getPosition() == originalStreamPos); } } return nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h000066400000000000000000000126261320201440200336310ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOFORMATMANAGER_H_INCLUDED #define JUCE_AUDIOFORMATMANAGER_H_INCLUDED //============================================================================== /** A class for keeping a list of available audio formats, and for deciding which one to use to open a given file. After creating an AudioFormatManager object, you should call registerFormat() or registerBasicFormats() to give it a list of format types that it can use. @see AudioFormat */ class JUCE_API AudioFormatManager { public: //============================================================================== /** Creates an empty format manager. Before it'll be any use, you'll need to call registerFormat() with all the formats you want it to be able to recognise. */ AudioFormatManager(); /** Destructor. */ ~AudioFormatManager(); //============================================================================== /** Adds a format to the manager's list of available file types. The object passed-in will be deleted by this object, so don't keep a pointer to it! If makeThisTheDefaultFormat is true, then the getDefaultFormat() method will return this one when called. */ void registerFormat (AudioFormat* newFormat, bool makeThisTheDefaultFormat); /** Handy method to make it easy to register the formats that come with Juce. Currently, this will add WAV and AIFF to the list. */ void registerBasicFormats(); /** Clears the list of known formats. */ void clearFormats(); /** Returns the number of currently registered file formats. */ int getNumKnownFormats() const; /** Returns one of the registered file formats. */ AudioFormat* getKnownFormat (int index) const; /** Iterator access to the list of known formats. */ AudioFormat** begin() const noexcept { return knownFormats.begin(); } /** Iterator access to the list of known formats. */ AudioFormat** end() const noexcept { return knownFormats.end(); } /** Looks for which of the known formats is listed as being for a given file extension. The extension may have a dot before it, so e.g. ".wav" or "wav" are both ok. */ AudioFormat* findFormatForFileExtension (const String& fileExtension) const; /** Returns the format which has been set as the default one. You can set a format as being the default when it is registered. It's useful when you want to write to a file, because the best format may change between platforms, e.g. AIFF is preferred on the Mac, WAV on Windows. If none has been set as the default, this method will just return the first one in the list. */ AudioFormat* getDefaultFormat() const; /** Returns a set of wildcards for file-matching that contains the extensions for all known formats. E.g. if might return "*.wav;*.aiff" if it just knows about wavs and aiffs. */ String getWildcardForAllFormats() const; //============================================================================== /** Searches through the known formats to try to create a suitable reader for this file. If none of the registered formats can open the file, it'll return 0. If it returns a reader, it's the caller's responsibility to delete the reader. */ AudioFormatReader* createReaderFor (const File& audioFile); /** Searches through the known formats to try to create a suitable reader for this stream. The stream object that is passed-in will be deleted by this method or by the reader that is returned, so the caller should not keep any references to it. The stream that is passed-in must be capable of being repositioned so that all the formats can have a go at opening it. If none of the registered formats can open the stream, it'll return 0. If it returns a reader, it's the caller's responsibility to delete the reader. */ AudioFormatReader* createReaderFor (InputStream* audioFileStream); private: //============================================================================== OwnedArray knownFormats; int defaultFormatIndex; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatManager) }; #endif // JUCE_AUDIOFORMATMANAGER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp000066400000000000000000000352061320201440200340130ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioFormatReader::AudioFormatReader (InputStream* const in, const String& name) : sampleRate (0), bitsPerSample (0), lengthInSamples (0), numChannels (0), usesFloatingPointData (false), input (in), formatName (name) { } AudioFormatReader::~AudioFormatReader() { delete input; } bool AudioFormatReader::read (int* const* destSamples, int numDestChannels, int64 startSampleInSource, int numSamplesToRead, const bool fillLeftoverChannelsWithCopies) { jassert (numDestChannels > 0); // you have to actually give this some channels to work with! const size_t originalNumSamplesToRead = (size_t) numSamplesToRead; int startOffsetInDestBuffer = 0; if (startSampleInSource < 0) { const int silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead); for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) zeromem (destSamples[i], sizeof (int) * (size_t) silence); startOffsetInDestBuffer += silence; numSamplesToRead -= silence; startSampleInSource = 0; } if (numSamplesToRead <= 0) return true; if (! readSamples (const_cast (destSamples), jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer, startSampleInSource, numSamplesToRead)) return false; if (numDestChannels > (int) numChannels) { if (fillLeftoverChannelsWithCopies) { int* lastFullChannel = destSamples[0]; for (int i = (int) numChannels; --i > 0;) { if (destSamples[i] != nullptr) { lastFullChannel = destSamples[i]; break; } } if (lastFullChannel != nullptr) for (int i = (int) numChannels; i < numDestChannels; ++i) if (destSamples[i] != nullptr) memcpy (destSamples[i], lastFullChannel, sizeof (int) * originalNumSamplesToRead); } else { for (int i = (int) numChannels; i < numDestChannels; ++i) if (destSamples[i] != nullptr) zeromem (destSamples[i], sizeof (int) * originalNumSamplesToRead); } } return true; } static void readChannels (AudioFormatReader& reader, int** const chans, AudioSampleBuffer* const buffer, const int startSample, const int numSamples, const int64 readerStartSample, const int numTargetChannels) { for (int j = 0; j < numTargetChannels; ++j) chans[j] = reinterpret_cast (buffer->getWritePointer (j, startSample)); chans[numTargetChannels] = nullptr; reader.read (chans, numTargetChannels, readerStartSample, numSamples, true); } void AudioFormatReader::read (AudioSampleBuffer* buffer, int startSample, int numSamples, int64 readerStartSample, bool useReaderLeftChan, bool useReaderRightChan) { jassert (buffer != nullptr); jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples()); if (numSamples > 0) { const int numTargetChannels = buffer->getNumChannels(); if (numTargetChannels <= 2) { int* const dest0 = reinterpret_cast (buffer->getWritePointer (0, startSample)); int* const dest1 = reinterpret_cast (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr); int* chans[3]; if (useReaderLeftChan == useReaderRightChan) { chans[0] = dest0; chans[1] = numChannels > 1 ? dest1 : nullptr; } else if (useReaderLeftChan || (numChannels == 1)) { chans[0] = dest0; chans[1] = nullptr; } else if (useReaderRightChan) { chans[0] = nullptr; chans[1] = dest0; } chans[2] = nullptr; read (chans, 2, readerStartSample, numSamples, true); // if the target's stereo and the source is mono, dupe the first channel.. if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr)) memcpy (dest1, dest0, sizeof (float) * (size_t) numSamples); } else if (numTargetChannels <= 64) { int* chans[65]; readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels); } else { HeapBlock chans ((size_t) numTargetChannels + 1); readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels); } if (! usesFloatingPointData) for (int j = 0; j < numTargetChannels; ++j) if (float* const d = buffer->getWritePointer (j, startSample)) FloatVectorOperations::convertFixedToFloat (d, reinterpret_cast (d), 1.0f / 0x7fffffff, numSamples); } } void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* const results, const int channelsToRead) { jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels); if (numSamples <= 0) { for (int i = 0; i < channelsToRead; ++i) results[i] = Range(); return; } const int bufferSize = (int) jmin (numSamples, (int64) 4096); AudioSampleBuffer tempSampleBuffer ((int) channelsToRead, bufferSize); float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers(); int* const* intBuffer = reinterpret_cast (floatBuffer); bool isFirstBlock = true; while (numSamples > 0) { const int numToDo = (int) jmin (numSamples, (int64) bufferSize); if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false)) break; for (int i = 0; i < channelsToRead; ++i) { Range r; if (usesFloatingPointData) { r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo); } else { Range intRange (Range::findMinAndMax (intBuffer[i], numToDo)); r = Range (intRange.getStart() / (float) std::numeric_limits::max(), intRange.getEnd() / (float) std::numeric_limits::max()); } results[i] = isFirstBlock ? r : results[i].getUnionWith (r); } isFirstBlock = false; numSamples -= numToDo; startSampleInFile += numToDo; } } void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, float& lowestLeft, float& highestLeft, float& lowestRight, float& highestRight) { Range levels[2]; if (numChannels < 2) { readMaxLevels (startSampleInFile, numSamples, levels, (int) numChannels); levels[1] = levels[0]; } else { readMaxLevels (startSampleInFile, numSamples, levels, 2); } lowestLeft = levels[0].getStart(); highestLeft = levels[0].getEnd(); lowestRight = levels[1].getStart(); highestRight = levels[1].getEnd(); } int64 AudioFormatReader::searchForLevel (int64 startSample, int64 numSamplesToSearch, const double magnitudeRangeMinimum, const double magnitudeRangeMaximum, const int minimumConsecutiveSamples) { if (numSamplesToSearch == 0) return -1; const int bufferSize = 4096; HeapBlock tempSpace (bufferSize * 2 + 64); int* tempBuffer[3]; tempBuffer[0] = tempSpace.getData(); tempBuffer[1] = tempSpace.getData() + bufferSize; tempBuffer[2] = 0; int consecutive = 0; int64 firstMatchPos = -1; jassert (magnitudeRangeMaximum > magnitudeRangeMinimum); const double doubleMin = jlimit (0.0, (double) std::numeric_limits::max(), magnitudeRangeMinimum * std::numeric_limits::max()); const double doubleMax = jlimit (doubleMin, (double) std::numeric_limits::max(), magnitudeRangeMaximum * std::numeric_limits::max()); const int intMagnitudeRangeMinimum = roundToInt (doubleMin); const int intMagnitudeRangeMaximum = roundToInt (doubleMax); while (numSamplesToSearch != 0) { const int numThisTime = (int) jmin (abs64 (numSamplesToSearch), (int64) bufferSize); int64 bufferStart = startSample; if (numSamplesToSearch < 0) bufferStart -= numThisTime; if (bufferStart >= (int) lengthInSamples) break; read (tempBuffer, 2, bufferStart, numThisTime, false); int num = numThisTime; while (--num >= 0) { if (numSamplesToSearch < 0) --startSample; bool matches = false; const int index = (int) (startSample - bufferStart); if (usesFloatingPointData) { const float sample1 = std::abs (((float*) tempBuffer[0]) [index]); if (sample1 >= magnitudeRangeMinimum && sample1 <= magnitudeRangeMaximum) { matches = true; } else if (numChannels > 1) { const float sample2 = std::abs (((float*) tempBuffer[1]) [index]); matches = (sample2 >= magnitudeRangeMinimum && sample2 <= magnitudeRangeMaximum); } } else { const int sample1 = abs (tempBuffer[0] [index]); if (sample1 >= intMagnitudeRangeMinimum && sample1 <= intMagnitudeRangeMaximum) { matches = true; } else if (numChannels > 1) { const int sample2 = abs (tempBuffer[1][index]); matches = (sample2 >= intMagnitudeRangeMinimum && sample2 <= intMagnitudeRangeMaximum); } } if (matches) { if (firstMatchPos < 0) firstMatchPos = startSample; if (++consecutive >= minimumConsecutiveSamples) { if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples) return -1; return firstMatchPos; } } else { consecutive = 0; firstMatchPos = -1; } if (numSamplesToSearch > 0) ++startSample; } if (numSamplesToSearch > 0) numSamplesToSearch -= numThisTime; else numSamplesToSearch += numThisTime; } return -1; } //============================================================================== MemoryMappedAudioFormatReader::MemoryMappedAudioFormatReader (const File& f, const AudioFormatReader& reader, int64 start, int64 length, int frameSize) : AudioFormatReader (nullptr, reader.getFormatName()), file (f), dataChunkStart (start), dataLength (length), bytesPerFrame (frameSize) { sampleRate = reader.sampleRate; bitsPerSample = reader.bitsPerSample; lengthInSamples = reader.lengthInSamples; numChannels = reader.numChannels; metadataValues = reader.metadataValues; usesFloatingPointData = reader.usesFloatingPointData; } bool MemoryMappedAudioFormatReader::mapEntireFile() { return mapSectionOfFile (Range (0, lengthInSamples)); } bool MemoryMappedAudioFormatReader::mapSectionOfFile (Range samplesToMap) { if (map == nullptr || samplesToMap != mappedSection) { map = nullptr; const Range fileRange (sampleToFilePos (samplesToMap.getStart()), sampleToFilePos (samplesToMap.getEnd())); map = new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly); if (map->getData() == nullptr) map = nullptr; else mappedSection = Range (jmax ((int64) 0, filePosToSample (map->getRange().getStart() + (bytesPerFrame - 1))), jmin (lengthInSamples, filePosToSample (map->getRange().getEnd()))); } return map != nullptr; } static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation void MemoryMappedAudioFormatReader::touchSample (int64 sample) const noexcept { if (map != nullptr && mappedSection.contains (sample)) memoryReadDummyVariable += *(char*) sampleToPointer (sample); else jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h000066400000000000000000000364411320201440200334620ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOFORMATREADER_H_INCLUDED #define JUCE_AUDIOFORMATREADER_H_INCLUDED //============================================================================== /** Reads samples from an audio file stream. A subclass that reads a specific type of audio format will be created by an AudioFormat object. @see AudioFormat, AudioFormatWriter */ class JUCE_API AudioFormatReader { protected: //============================================================================== /** Creates an AudioFormatReader object. @param sourceStream the stream to read from - this will be deleted by this object when it is no longer needed. (Some specialised readers might not use this parameter and can leave it as nullptr). @param formatName the description that will be returned by the getFormatName() method */ AudioFormatReader (InputStream* sourceStream, const String& formatName); public: /** Destructor. */ virtual ~AudioFormatReader(); //============================================================================== /** Returns a description of what type of format this is. E.g. "AIFF" */ const String& getFormatName() const noexcept { return formatName; } //============================================================================== /** Reads samples from the stream. @param destSamples an array of buffers into which the sample data for each channel will be written. If the format is fixed-point, each channel will be written as an array of 32-bit signed integers using the full range -0x80000000 to 0x7fffffff, regardless of the source's bit-depth. If it is a floating-point format, you should cast the resulting array to a (float**) to get the values (in the range -1.0 to 1.0 or beyond) If the format is stereo, then destSamples[0] is the left channel data, and destSamples[1] is the right channel. The numDestChannels parameter indicates how many pointers this array contains, but some of these pointers can be null if you don't want to read data for some of the channels @param numDestChannels the number of array elements in the destChannels array @param startSampleInSource the position in the audio file or stream at which the samples should be read, as a number of samples from the start of the stream. It's ok for this to be beyond the start or end of the available data - any samples that are out-of-range will be returned as zeros. @param numSamplesToRead the number of samples to read. If this is greater than the number of samples that the file or stream contains. the result will be padded with zeros @param fillLeftoverChannelsWithCopies if true, this indicates that if there's no source data available for some of the channels that you pass in, then they should be filled with copies of valid source channels. E.g. if you're reading a mono file and you pass 2 channels to this method, then if fillLeftoverChannelsWithCopies is true, both destination channels will be filled with the same data from the file's single channel. If fillLeftoverChannelsWithCopies was false, then only the first channel would be filled with the file's contents, and the second would be cleared. If there are many channels, e.g. you try to read 4 channels from a stereo file, then the last 3 would all end up with copies of the same data. @returns true if the operation succeeded, false if there was an error. Note that reading sections of data beyond the extent of the stream isn't an error - the reader should just return zeros for these regions @see readMaxLevels */ bool read (int* const* destSamples, int numDestChannels, int64 startSampleInSource, int numSamplesToRead, bool fillLeftoverChannelsWithCopies); /** Fills a section of an AudioSampleBuffer from this reader. This will convert the reader's fixed- or floating-point data to the buffer's floating-point format, and will try to intelligently cope with mismatches between the number of channels in the reader and the buffer. */ void read (AudioSampleBuffer* buffer, int startSampleInDestBuffer, int numSamples, int64 readerStartSample, bool useReaderLeftChan, bool useReaderRightChan); /** Finds the highest and lowest sample levels from a section of the audio stream. This will read a block of samples from the stream, and measure the highest and lowest sample levels from the channels in that section, returning these as normalised floating-point levels. @param startSample the offset into the audio stream to start reading from. It's ok for this to be beyond the start or end of the stream. @param numSamples how many samples to read @param results this array will be filled with Range values for each channel. The array must contain numChannels elements. @param numChannelsToRead the number of channels of data to scan. This must be more than zero, but not more than the total number of channels that the reader contains @see read */ virtual void readMaxLevels (int64 startSample, int64 numSamples, Range* results, int numChannelsToRead); /** Finds the highest and lowest sample levels from a section of the audio stream. This will read a block of samples from the stream, and measure the highest and lowest sample levels from the channels in that section, returning these as normalised floating-point levels. @param startSample the offset into the audio stream to start reading from. It's ok for this to be beyond the start or end of the stream. @param numSamples how many samples to read @param lowestLeft on return, this is the lowest absolute sample from the left channel @param highestLeft on return, this is the highest absolute sample from the left channel @param lowestRight on return, this is the lowest absolute sample from the right channel (if there is one) @param highestRight on return, this is the highest absolute sample from the right channel (if there is one) @see read */ virtual void readMaxLevels (int64 startSample, int64 numSamples, float& lowestLeft, float& highestLeft, float& lowestRight, float& highestRight); /** Scans the source looking for a sample whose magnitude is in a specified range. This will read from the source, either forwards or backwards between two sample positions, until it finds a sample whose magnitude lies between two specified levels. If it finds a suitable sample, it returns its position; if not, it will return -1. There's also a minimumConsecutiveSamples setting to help avoid spikes or zero-crossing points when you're searching for a continuous range of samples @param startSample the first sample to look at @param numSamplesToSearch the number of samples to scan. If this value is negative, the search will go backwards @param magnitudeRangeMinimum the lowest magnitude (inclusive) that is considered a hit, from 0 to 1.0 @param magnitudeRangeMaximum the highest magnitude (inclusive) that is considered a hit, from 0 to 1.0 @param minimumConsecutiveSamples if this is > 0, the method will only look for a sequence of this many consecutive samples, all of which lie within the target range. When it finds such a sequence, it returns the position of the first in-range sample it found (i.e. the earliest one if scanning forwards, the latest one if scanning backwards) */ int64 searchForLevel (int64 startSample, int64 numSamplesToSearch, double magnitudeRangeMinimum, double magnitudeRangeMaximum, int minimumConsecutiveSamples); //============================================================================== /** The sample-rate of the stream. */ double sampleRate; /** The number of bits per sample, e.g. 16, 24, 32. */ unsigned int bitsPerSample; /** The total number of samples in the audio stream. */ int64 lengthInSamples; /** The total number of channels in the audio stream. */ unsigned int numChannels; /** Indicates whether the data is floating-point or fixed. */ bool usesFloatingPointData; /** A set of metadata values that the reader has pulled out of the stream. Exactly what these values are depends on the format, so you can check out the format implementation code to see what kind of stuff they understand. */ StringPairArray metadataValues; /** The input stream, for use by subclasses. */ InputStream* input; //============================================================================== /** Subclasses must implement this method to perform the low-level read operation. Callers should use read() instead of calling this directly. @param destSamples the array of destination buffers to fill. Some of these pointers may be null @param numDestChannels the number of items in the destSamples array. This value is guaranteed not to be greater than the number of channels that this reader object contains @param startOffsetInDestBuffer the number of samples from the start of the dest data at which to begin writing @param startSampleInFile the number of samples into the source data at which to begin reading. This value is guaranteed to be >= 0. @param numSamples the number of samples to read */ virtual bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) = 0; protected: //============================================================================== /** Used by AudioFormatReader subclasses to copy data to different formats. */ template struct ReadHelper { typedef AudioData::Pointer DestType; typedef AudioData::Pointer SourceType; template static void read (TargetType* const* destData, int destOffset, int numDestChannels, const void* sourceData, int numSourceChannels, int numSamples) noexcept { for (int i = 0; i < numDestChannels; ++i) { if (void* targetChan = destData[i]) { DestType dest (targetChan); dest += destOffset; if (i < numSourceChannels) dest.convertSamples (SourceType (addBytesToPointer (sourceData, i * SourceType::getBytesPerSample()), numSourceChannels), numSamples); else dest.clearSamples (numSamples); } } } }; /** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie beyond the end of their available length. */ static void clearSamplesBeyondAvailableLength (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int& numSamples, int64 fileLengthInSamples) { jassert (destSamples != nullptr); const int64 samplesAvailable = fileLengthInSamples - startSampleInFile; if (samplesAvailable < numSamples) { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); numSamples = (int) samplesAvailable; } } private: String formatName; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReader) }; #endif // JUCE_AUDIOFORMATREADER_H_INCLUDED juce_AudioFormatReaderSource.cpp000066400000000000000000000061321320201440200351110ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioFormatReaderSource::AudioFormatReaderSource (AudioFormatReader* const r, const bool deleteReaderWhenThisIsDeleted) : reader (r, deleteReaderWhenThisIsDeleted), nextPlayPos (0), looping (false) { jassert (reader != nullptr); } AudioFormatReaderSource::~AudioFormatReaderSource() {} int64 AudioFormatReaderSource::getTotalLength() const { return reader->lengthInSamples; } void AudioFormatReaderSource::setNextReadPosition (int64 newPosition) { nextPlayPos = newPosition; } void AudioFormatReaderSource::setLooping (bool shouldLoop) { looping = shouldLoop; } int64 AudioFormatReaderSource::getNextReadPosition() const { return looping ? nextPlayPos % reader->lengthInSamples : nextPlayPos; } void AudioFormatReaderSource::prepareToPlay (int /*samplesPerBlockExpected*/, double /*sampleRate*/) {} void AudioFormatReaderSource::releaseResources() {} void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { if (info.numSamples > 0) { const int64 start = nextPlayPos; if (looping) { const int64 newStart = start % reader->lengthInSamples; const int64 newEnd = (start + info.numSamples) % reader->lengthInSamples; if (newEnd > newStart) { reader->read (info.buffer, info.startSample, (int) (newEnd - newStart), newStart, true, true); } else { const int endSamps = (int) (reader->lengthInSamples - newStart); reader->read (info.buffer, info.startSample, endSamps, newStart, true, true); reader->read (info.buffer, info.startSample + endSamps, (int) newEnd, 0, true, true); } nextPlayPos = newEnd; } else { reader->read (info.buffer, info.startSample, info.numSamples, start, true, true); nextPlayPos += info.numSamples; } } } juce_AudioFormatReaderSource.h000066400000000000000000000076451320201440200345700ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOFORMATREADERSOURCE_H_INCLUDED #define JUCE_AUDIOFORMATREADERSOURCE_H_INCLUDED //============================================================================== /** A type of AudioSource that will read from an AudioFormatReader. @see PositionableAudioSource, AudioTransportSource, BufferingAudioSource */ class JUCE_API AudioFormatReaderSource : public PositionableAudioSource { public: //============================================================================== /** Creates an AudioFormatReaderSource for a given reader. @param sourceReader the reader to use as the data source - this must not be null @param deleteReaderWhenThisIsDeleted if true, the reader passed-in will be deleted when this object is deleted; if false it will be left up to the caller to manage its lifetime */ AudioFormatReaderSource (AudioFormatReader* sourceReader, bool deleteReaderWhenThisIsDeleted); /** Destructor. */ ~AudioFormatReaderSource(); //============================================================================== /** Toggles loop-mode. If set to true, it will continuously loop the input source. If false, it will just emit silence after the source has finished. @see isLooping */ void setLooping (bool shouldLoop) override; /** Returns whether loop-mode is turned on or not. */ bool isLooping() const override { return looping; } /** Returns the reader that's being used. */ AudioFormatReader* getAudioFormatReader() const noexcept { return reader; } //============================================================================== /** Implementation of the AudioSource method. */ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ void releaseResources() override; /** Implementation of the AudioSource method. */ void getNextAudioBlock (const AudioSourceChannelInfo&) override; //============================================================================== /** Implements the PositionableAudioSource method. */ void setNextReadPosition (int64 newPosition) override; /** Implements the PositionableAudioSource method. */ int64 getNextReadPosition() const override; /** Implements the PositionableAudioSource method. */ int64 getTotalLength() const override; private: //============================================================================== OptionalScopedPointer reader; int64 volatile nextPlayPos; bool volatile looping; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReaderSource) }; #endif // JUCE_AUDIOFORMATREADERSOURCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp000066400000000000000000000241751320201440200340700ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioFormatWriter::AudioFormatWriter (OutputStream* const out, const String& formatName_, const double rate, const unsigned int numChannels_, const unsigned int bitsPerSample_) : sampleRate (rate), numChannels (numChannels_), bitsPerSample (bitsPerSample_), usesFloatingPointData (false), output (out), formatName (formatName_) { } AudioFormatWriter::~AudioFormatWriter() { delete output; } static void convertFloatsToInts (int* dest, const float* src, int numSamples) noexcept { while (--numSamples >= 0) { const double samp = *src++; if (samp <= -1.0) *dest = std::numeric_limits::min(); else if (samp >= 1.0) *dest = std::numeric_limits::max(); else *dest = roundToInt (std::numeric_limits::max() * samp); ++dest; } } bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, int64 startSample, int64 numSamplesToRead) { const int bufferSize = 16384; AudioSampleBuffer tempBuffer ((int) numChannels, bufferSize); int* buffers [128] = { 0 }; for (int i = tempBuffer.getNumChannels(); --i >= 0;) buffers[i] = reinterpret_cast (tempBuffer.getWritePointer (i, 0)); if (numSamplesToRead < 0) numSamplesToRead = reader.lengthInSamples; while (numSamplesToRead > 0) { const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize); if (! reader.read (buffers, (int) numChannels, startSample, numToDo, false)) return false; if (reader.usesFloatingPointData != isFloatingPoint()) { int** bufferChan = buffers; while (*bufferChan != nullptr) { void* const b = *bufferChan++; if (isFloatingPoint()) FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, 1.0f / 0x7fffffff, numToDo); else convertFloatsToInts ((int*) b, (float*) b, numToDo); } } if (! write (const_cast (buffers), numToDo)) return false; numSamplesToRead -= numToDo; startSample += numToDo; } return true; } bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock) { AudioSampleBuffer tempBuffer (getNumChannels(), samplesPerBlock); while (numSamplesToRead > 0) { const int numToDo = jmin (numSamplesToRead, samplesPerBlock); AudioSourceChannelInfo info (&tempBuffer, 0, numToDo); info.clearActiveBufferRegion(); source.getNextAudioBlock (info); if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo)) return false; numSamplesToRead -= numToDo; } return true; } bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples) { if (numSamples <= 0) return true; if (isFloatingPoint()) return write ((const int**) channels, numSamples); int* chans [256]; int scratch [4096]; jassert (numSourceChannels < numElementsInArray (chans)); const int maxSamples = (int) (numElementsInArray (scratch) / numSourceChannels); for (int i = 0; i < numSourceChannels; ++i) chans[i] = scratch + (i * maxSamples); chans[numSourceChannels] = nullptr; int startSample = 0; while (numSamples > 0) { const int numToDo = jmin (numSamples, maxSamples); for (int i = 0; i < numSourceChannels; ++i) convertFloatsToInts (chans[i], channels[i] + startSample, numToDo); if (! write ((const int**) chans, numToDo)) return false; startSample += numToDo; numSamples -= numToDo; } return true; } bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples) { const int numSourceChannels = source.getNumChannels(); jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0); if (startSample == 0) return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples); const float* chans [256]; jassert ((int) numChannels < numElementsInArray (chans)); for (int i = 0; i < numSourceChannels; ++i) chans[i] = source.getReadPointer (i, startSample); chans[numSourceChannels] = nullptr; return writeFromFloatArrays (chans, numSourceChannels, numSamples); } bool AudioFormatWriter::flush() { return false; } //============================================================================== class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient { public: Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples) : fifo (numSamples), buffer (channels, numSamples), timeSliceThread (tst), writer (w), receiver (nullptr), samplesWritten (0), samplesPerFlush (0), flushSampleCounter (0), isRunning (true) { timeSliceThread.addTimeSliceClient (this); } ~Buffer() { isRunning = false; timeSliceThread.removeTimeSliceClient (this); while (writePendingData() == 0) {} } bool write (const float* const* data, int numSamples) { if (numSamples <= 0 || ! isRunning) return true; jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this! int start1, size1, start2, size2; fifo.prepareToWrite (numSamples, start1, size1, start2, size2); if (size1 + size2 < numSamples) return false; for (int i = buffer.getNumChannels(); --i >= 0;) { buffer.copyFrom (i, start1, data[i], size1); buffer.copyFrom (i, start2, data[i] + size1, size2); } fifo.finishedWrite (size1 + size2); timeSliceThread.notify(); return true; } int useTimeSlice() override { return writePendingData(); } int writePendingData() { const int numToDo = fifo.getTotalSize() / 4; int start1, size1, start2, size2; fifo.prepareToRead (numToDo, start1, size1, start2, size2); if (size1 <= 0) return 10; writer->writeFromAudioSampleBuffer (buffer, start1, size1); const ScopedLock sl (thumbnailLock); if (receiver != nullptr) receiver->addBlock (samplesWritten, buffer, start1, size1); samplesWritten += size1; if (size2 > 0) { writer->writeFromAudioSampleBuffer (buffer, start2, size2); if (receiver != nullptr) receiver->addBlock (samplesWritten, buffer, start2, size2); samplesWritten += size2; } fifo.finishedRead (size1 + size2); if (samplesPerFlush > 0) { flushSampleCounter -= size1 + size2; if (flushSampleCounter <= 0) { flushSampleCounter = samplesPerFlush; writer->flush(); } } return 0; } void setDataReceiver (IncomingDataReceiver* newReceiver) { if (newReceiver != nullptr) newReceiver->reset (buffer.getNumChannels(), writer->getSampleRate(), 0); const ScopedLock sl (thumbnailLock); receiver = newReceiver; samplesWritten = 0; } void setFlushInterval (int numSamples) noexcept { samplesPerFlush = numSamples; } private: AbstractFifo fifo; AudioSampleBuffer buffer; TimeSliceThread& timeSliceThread; ScopedPointer writer; CriticalSection thumbnailLock; IncomingDataReceiver* receiver; int64 samplesWritten; int samplesPerFlush, flushSampleCounter; volatile bool isRunning; JUCE_DECLARE_NON_COPYABLE (Buffer) }; AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer) : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer)) { } AudioFormatWriter::ThreadedWriter::~ThreadedWriter() { } bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples) { return buffer->write (data, numSamples); } void AudioFormatWriter::ThreadedWriter::setDataReceiver (AudioFormatWriter::ThreadedWriter::IncomingDataReceiver* receiver) { buffer->setDataReceiver (receiver); } void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept { buffer->setFlushInterval (numSamplesPerFlush); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h000066400000000000000000000276241320201440200335370ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOFORMATWRITER_H_INCLUDED #define JUCE_AUDIOFORMATWRITER_H_INCLUDED //============================================================================== /** Writes samples to an audio file stream. A subclass that writes a specific type of audio format will be created by an AudioFormat object. After creating one of these with the AudioFormat::createWriterFor() method you can call its write() method to store the samples, and then delete it. @see AudioFormat, AudioFormatReader */ class JUCE_API AudioFormatWriter { protected: //============================================================================== /** Creates an AudioFormatWriter object. @param destStream the stream to write to - this will be deleted by this object when it is no longer needed @param formatName the description that will be returned by the getFormatName() method @param sampleRate the sample rate to use - the base class just stores this value, it doesn't do anything with it @param numberOfChannels the number of channels to write - the base class just stores this value, it doesn't do anything with it @param bitsPerSample the bit depth of the stream - the base class just stores this value, it doesn't do anything with it */ AudioFormatWriter (OutputStream* destStream, const String& formatName, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample); public: /** Destructor. */ virtual ~AudioFormatWriter(); //============================================================================== /** Returns a description of what type of format this is. E.g. "AIFF file" */ const String& getFormatName() const noexcept { return formatName; } //============================================================================== /** Writes a set of samples to the audio stream. Note that if you're trying to write the contents of an AudioSampleBuffer, you can use AudioSampleBuffer::writeToAudioWriter(). @param samplesToWrite an array of arrays containing the sample data for each channel to write. This is a zero-terminated array of arrays, and can contain a different number of channels than the actual stream uses, and the writer should do its best to cope with this. If the format is fixed-point, each channel will be formatted as an array of signed integers using the full 32-bit range -0x80000000 to 0x7fffffff, regardless of the source's bit-depth. If it is a floating-point format, you should treat the arrays as arrays of floats, and just cast it to an (int**) to pass it into the method. @param numSamples the number of samples to write */ virtual bool write (const int** samplesToWrite, int numSamples) = 0; /** Some formats may support a flush operation that makes sure the file is in a valid state before carrying on. If supported, this means that by calling flush periodically when writing data to a large file, then it should still be left in a readable state if your program crashes. It goes without saying that this method must be called from the same thread that's calling write()! If the format supports flushing and the operation succeeds, this returns true. */ virtual bool flush(); //============================================================================== /** Reads a section of samples from an AudioFormatReader, and writes these to the output. This will take care of any floating-point conversion that's required to convert between the two formats. It won't deal with sample-rate conversion, though. If numSamplesToRead < 0, it will write the entire length of the reader. @returns false if it can't read or write properly during the operation */ bool writeFromAudioReader (AudioFormatReader& reader, int64 startSample, int64 numSamplesToRead); /** Reads some samples from an AudioSource, and writes these to the output. The source must already have been initialised with the AudioSource::prepareToPlay() method @param source the source to read from @param numSamplesToRead total number of samples to read and write @param samplesPerBlock the maximum number of samples to fetch from the source @returns false if it can't read or write properly during the operation */ bool writeFromAudioSource (AudioSource& source, int numSamplesToRead, int samplesPerBlock = 2048); /** Writes some samples from an AudioSampleBuffer. */ bool writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples); /** Writes some samples from a set of float data channels. */ bool writeFromFloatArrays (const float* const* channels, int numChannels, int numSamples); //============================================================================== /** Returns the sample rate being used. */ double getSampleRate() const noexcept { return sampleRate; } /** Returns the number of channels being written. */ int getNumChannels() const noexcept { return (int) numChannels; } /** Returns the bit-depth of the data being written. */ int getBitsPerSample() const noexcept { return (int) bitsPerSample; } /** Returns true if it's a floating-point format, false if it's fixed-point. */ bool isFloatingPoint() const noexcept { return usesFloatingPointData; } //============================================================================== /** Provides a FIFO for an AudioFormatWriter, allowing you to push incoming data into a buffer which will be flushed to disk by a background thread. */ class ThreadedWriter { public: /** Creates a ThreadedWriter for a given writer and a thread. The writer object which is passed in here will be owned and deleted by the ThreadedWriter when it is no longer needed. To stop the writer and flush the buffer to disk, simply delete this object. */ ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer); /** Destructor. */ ~ThreadedWriter(); /** Pushes some incoming audio data into the FIFO. If there's enough free space in the buffer, this will add the data to it, If the FIFO is too full to accept this many samples, the method will return false - then you could either wait until the background thread has had time to consume some of the buffered data and try again, or you can give up and lost this block. The data must be an array containing the same number of channels as the AudioFormatWriter object is using. None of these channels can be null. */ bool write (const float* const* data, int numSamples); class JUCE_API IncomingDataReceiver { public: IncomingDataReceiver() {} virtual ~IncomingDataReceiver() {} virtual void reset (int numChannels, double sampleRate, int64 totalSamplesInSource) = 0; virtual void addBlock (int64 sampleNumberInSource, const AudioSampleBuffer& newData, int startOffsetInBuffer, int numSamples) = 0; }; /** Allows you to specify a callback that this writer should update with the incoming data. The receiver will be cleared and will the writer will begin adding data to it as the data arrives. Pass a null pointer to remove the current receiver. The object passed-in must not be deleted while this writer is still using it. */ void setDataReceiver (IncomingDataReceiver*); /** Sets how many samples should be written before calling the AudioFormatWriter::flush method. Set this to 0 to disable flushing (this is the default). */ void setFlushInterval (int numSamplesPerFlush) noexcept; private: class Buffer; friend struct ContainerDeletePolicy; ScopedPointer buffer; }; protected: //============================================================================== /** The sample rate of the stream. */ double sampleRate; /** The number of channels being written to the stream. */ unsigned int numChannels; /** The bit depth of the file. */ unsigned int bitsPerSample; /** True if it's a floating-point format, false if it's fixed-point. */ bool usesFloatingPointData; /** The output stream for use by subclasses. */ OutputStream* output; /** Used by AudioFormatWriter subclasses to copy data to different formats. */ template struct WriteHelper { typedef AudioData::Pointer DestType; typedef AudioData::Pointer SourceType; static void write (void* destData, int numDestChannels, const int* const* source, int numSamples, const int sourceOffset = 0) noexcept { for (int i = 0; i < numDestChannels; ++i) { const DestType dest (addBytesToPointer (destData, i * DestType::getBytesPerSample()), numDestChannels); if (*source != nullptr) { dest.convertSamples (SourceType (*source + sourceOffset), numSamples); ++source; } else { dest.clearSamples (numSamples); } } } }; private: String formatName; friend class ThreadedWriter; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatWriter) }; #endif // JUCE_AUDIOFORMATWRITER_H_INCLUDED juce_AudioSubsectionReader.cpp000066400000000000000000000054401320201440200346170ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioSubsectionReader::AudioSubsectionReader (AudioFormatReader* const source_, const int64 startSample_, const int64 length_, const bool deleteSourceWhenDeleted_) : AudioFormatReader (0, source_->getFormatName()), source (source_), startSample (startSample_), deleteSourceWhenDeleted (deleteSourceWhenDeleted_) { length = jmin (jmax ((int64) 0, source->lengthInSamples - startSample), length_); sampleRate = source->sampleRate; bitsPerSample = source->bitsPerSample; lengthInSamples = length; numChannels = source->numChannels; usesFloatingPointData = source->usesFloatingPointData; } AudioSubsectionReader::~AudioSubsectionReader() { if (deleteSourceWhenDeleted) delete source; } //============================================================================== bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, length); return source->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile + startSample, numSamples); } void AudioSubsectionReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) { startSampleInFile = jmax ((int64) 0, startSampleInFile); numSamples = jmax ((int64) 0, jmin (numSamples, length - startSampleInFile)); source->readMaxLevels (startSampleInFile + startSample, numSamples, results, numChannelsToRead); } juce_AudioSubsectionReader.h000066400000000000000000000067661320201440200343000ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOSUBSECTIONREADER_H_INCLUDED #define JUCE_AUDIOSUBSECTIONREADER_H_INCLUDED //============================================================================== /** This class is used to wrap an AudioFormatReader and only read from a subsection of the file. So if you have a reader which can read a 1000 sample file, you could wrap it in one of these to only access, e.g. samples 100 to 200, and any samples outside that will come back as 0. Accessing sample 0 from this reader will actually read the first sample from the other's subsection, which might be at a non-zero position. @see AudioFormatReader */ class JUCE_API AudioSubsectionReader : public AudioFormatReader { public: //============================================================================== /** Creates an AudioSubsectionReader for a given data source. @param sourceReader the source reader from which we'll be taking data @param subsectionStartSample the sample within the source reader which will be mapped onto sample 0 for this reader. @param subsectionLength the number of samples from the source that will make up the subsection. If this reader is asked for any samples beyond this region, it will return zero. @param deleteSourceWhenDeleted if true, the sourceReader object will be deleted when this object is deleted. */ AudioSubsectionReader (AudioFormatReader* sourceReader, int64 subsectionStartSample, int64 subsectionLength, bool deleteSourceWhenDeleted); /** Destructor. */ ~AudioSubsectionReader(); //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override; void readMaxLevels (int64 startSample, int64 numSamples, Range* results, int numChannelsToRead) override; private: //============================================================================== AudioFormatReader* const source; int64 startSample, length; const bool deleteSourceWhenDeleted; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSubsectionReader) }; #endif // JUCE_AUDIOSUBSECTIONREADER_H_INCLUDED juce_BufferingAudioFormatReader.cpp000066400000000000000000000132651320201440200355650ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ BufferingAudioReader::BufferingAudioReader (AudioFormatReader* sourceReader, TimeSliceThread& timeSliceThread, int samplesToBuffer) : AudioFormatReader (nullptr, sourceReader->getFormatName()), source (sourceReader), thread (timeSliceThread), nextReadPosition (0), numBlocks (1 + (samplesToBuffer / samplesPerBlock)), timeoutMs (0) { sampleRate = source->sampleRate; lengthInSamples = source->lengthInSamples; numChannels = source->numChannels; metadataValues = source->metadataValues; bitsPerSample = 32; usesFloatingPointData = true; for (int i = 3; --i >= 0;) readNextBufferChunk(); timeSliceThread.addTimeSliceClient (this); } BufferingAudioReader::~BufferingAudioReader() { thread.removeTimeSliceClient (this); } void BufferingAudioReader::setReadTimeout (int timeoutMilliseconds) noexcept { timeoutMs = timeoutMilliseconds; } bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { const uint32 startTime = Time::getMillisecondCounter(); clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); const ScopedLock sl (lock); nextReadPosition = startSampleInFile; while (numSamples > 0) { if (const BufferedBlock* const block = getBlockContaining (startSampleInFile)) { const int offset = (int) (startSampleInFile - block->range.getStart()); const int numToDo = jmin (numSamples, (int) (block->range.getEnd() - startSampleInFile)); for (int j = 0; j < numDestChannels; ++j) { if (float* dest = (float*) destSamples[j]) { dest += startOffsetInDestBuffer; if (j < (int) numChannels) FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo); else FloatVectorOperations::clear (dest, numToDo); } } startOffsetInDestBuffer += numToDo; startSampleInFile += numToDo; numSamples -= numToDo; } else { if (timeoutMs >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) timeoutMs) { for (int j = 0; j < numDestChannels; ++j) if (float* dest = (float*) destSamples[j]) FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples); break; } else { ScopedUnlock ul (lock); Thread::yield(); } } } return true; } BufferingAudioReader::BufferedBlock::BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples) : range (pos, pos + numSamples), buffer ((int) reader.numChannels, numSamples) { reader.read (&buffer, 0, numSamples, pos, true, true); } BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos) const noexcept { for (int i = blocks.size(); --i >= 0;) { BufferedBlock* const b = blocks.getUnchecked(i); if (b->range.contains (pos)) return b; } return nullptr; } int BufferingAudioReader::useTimeSlice() { return readNextBufferChunk() ? 1 : 100; } bool BufferingAudioReader::readNextBufferChunk() { const int64 pos = nextReadPosition; const int64 startPos = ((pos - 1024) / samplesPerBlock) * samplesPerBlock; const int64 endPos = startPos + numBlocks * samplesPerBlock; OwnedArray newBlocks; for (int i = blocks.size(); --i >= 0;) if (blocks.getUnchecked(i)->range.intersects (Range (startPos, endPos))) newBlocks.add (blocks.getUnchecked(i)); if (newBlocks.size() == numBlocks) { newBlocks.clear (false); return false; } for (int64 p = startPos; p < endPos; p += samplesPerBlock) { if (getBlockContaining (p) == nullptr) { newBlocks.add (new BufferedBlock (*source, p, samplesPerBlock)); break; // just do one block } } { const ScopedLock sl (lock); newBlocks.swapWith (blocks); } for (int i = blocks.size(); --i >= 0;) newBlocks.removeObject (blocks.getUnchecked(i), false); return true; } juce_BufferingAudioFormatReader.h000066400000000000000000000065411320201440200352310ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_BUFFERINGAUDIOFORMATREADER_H_INCLUDED #define JUCE_BUFFERINGAUDIOFORMATREADER_H_INCLUDED //============================================================================== /** An AudioFormatReader that uses a background thread to pre-read data from another reader. @see AudioFormatReader */ class JUCE_API BufferingAudioReader : public AudioFormatReader, private TimeSliceClient { public: /** Creates a reader. @param sourceReader the source reader to wrap. This BufferingAudioReader takes ownership of this object and will delete it later when no longer needed @param timeSliceThread the thread that should be used to do the background reading. Make sure that the thread you supply is running, and won't be deleted while the reader object still exists. @param samplesToBuffer the total number of samples to buffer ahead. */ BufferingAudioReader (AudioFormatReader* sourceReader, TimeSliceThread& timeSliceThread, int samplesToBuffer); ~BufferingAudioReader(); /** Sets a number of milliseconds that the reader can block for in its readSamples() method before giving up and returning silence. A value of less that 0 means "wait forever". The default timeout is 0. */ void setReadTimeout (int timeoutMilliseconds) noexcept; bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override; private: ScopedPointer source; TimeSliceThread& thread; int64 nextReadPosition; const int numBlocks; int timeoutMs; enum { samplesPerBlock = 32768 }; struct BufferedBlock { BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples); Range range; AudioSampleBuffer buffer; }; CriticalSection lock; OwnedArray blocks; BufferedBlock* getBlockContaining (int64 pos) const noexcept; int useTimeSlice() override; bool readNextBufferChunk(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioReader) }; #endif // JUCE_BUFFERINGAUDIOFORMATREADER_H_INCLUDED juce_MemoryMappedAudioFormatReader.h000066400000000000000000000116531320201440200357210ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MEMORYMAPPEDAUDIOFORMATREADER_H_INCLUDED #define JUCE_MEMORYMAPPEDAUDIOFORMATREADER_H_INCLUDED //============================================================================== /** A specialised type of AudioFormatReader that uses a MemoryMappedFile to read directly from an audio file. This allows for incredibly fast random-access to sample data in the mapped region of the file, but not all audio formats support it - see AudioFormat::createMemoryMappedReader(). Note that before reading samples from a MemoryMappedAudioFormatReader, you must first call mapEntireFile() or mapSectionOfFile() to ensure that the region you want to read has been mapped. @see AudioFormat::createMemoryMappedReader, AudioFormatReader */ class JUCE_API MemoryMappedAudioFormatReader : public AudioFormatReader { protected: //============================================================================== /** Creates an MemoryMappedAudioFormatReader object. Note that before attempting to read any data, you must call mapEntireFile() or mapSectionOfFile() to ensure that the region you want to read has been mapped. */ MemoryMappedAudioFormatReader (const File& file, const AudioFormatReader& details, int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame); public: /** Returns the file that is being mapped */ const File& getFile() const noexcept { return file; } /** Attempts to map the entire file into memory. */ bool mapEntireFile(); /** Attempts to map a section of the file into memory. */ bool mapSectionOfFile (Range samplesToMap); /** Returns the sample range that's currently memory-mapped and available for reading. */ Range getMappedSection() const noexcept { return mappedSection; } /** Touches the memory for the given sample, to force it to be loaded into active memory. */ void touchSample (int64 sample) const noexcept; /** Returns the samples for all channels at a given sample position. The result array must be large enough to hold a value for each channel that this reader contains. */ virtual void getSample (int64 sampleIndex, float* result) const noexcept = 0; /** Returns the number of bytes currently being mapped */ size_t getNumBytesUsed() const { return map != nullptr ? map->getSize() : 0; } protected: File file; Range mappedSection; ScopedPointer map; int64 dataChunkStart, dataLength; int bytesPerFrame; /** Converts a sample index to a byte position in the file. */ inline int64 sampleToFilePos (int64 sample) const noexcept { return dataChunkStart + sample * bytesPerFrame; } /** Converts a byte position in the file to a sample index. */ inline int64 filePosToSample (int64 filePos) const noexcept { return (filePos - dataChunkStart) / bytesPerFrame; } /** Converts a sample index to a pointer to the mapped file memory. */ inline const void* sampleToPointer (int64 sample) const noexcept { return addBytesToPointer (map->getData(), sampleToFilePos (sample) - map->getRange().getStart()); } /** Used by AudioFormatReader subclasses to scan for min/max ranges in interleaved data. */ template Range scanMinAndMaxInterleaved (int channel, int64 startSampleInFile, int64 numSamples) const noexcept { typedef AudioData::Pointer SourceType; return SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), ((int) bitsPerSample / 8) * channel), (int) numChannels) .findMinAndMax ((size_t) numSamples); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAudioFormatReader) }; #endif // JUCE_MEMORYMAPPEDAUDIOFORMATREADER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp000066400000000000000000000103141320201440200320130ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if defined (JUCE_AUDIO_FORMATS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_audio_formats.h" //============================================================================== #if JUCE_MAC #define Point CarbonDummyPointName #define Component CarbonDummyCompName #if JUCE_QUICKTIME #import #endif #include #undef Component #undef Point #elif JUCE_IOS #import #import //============================================================================== #elif JUCE_WINDOWS #if JUCE_QUICKTIME /* If you've got an include error here, you probably need to install the QuickTime SDK and add its header directory to your include path. Alternatively, if you don't need any QuickTime services, just set the JUCE_QUICKTIME flag to 0. */ #include #include #include #include #include /* If you've got QuickTime 7 installed, then these COM objects should be found in the "\Program Files\Quicktime" directory. You'll need to add this directory to your include search path to make these import statements work. */ #import #import #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment (lib, "QTMLClient.lib") #endif #endif #if JUCE_USE_WINDOWS_MEDIA_FORMAT #include #endif #endif //============================================================================== namespace juce { #if JUCE_ANDROID #include "../juce_core/native/juce_android_JNIHelpers.h" #undef JUCE_QUICKTIME #endif #if JUCE_WINDOWS #include "../juce_core/native/juce_win32_ComSmartPtr.h" #endif #include "format/juce_AudioFormat.cpp" #include "format/juce_AudioFormatManager.cpp" #include "format/juce_AudioFormatReader.cpp" #include "format/juce_AudioFormatReaderSource.cpp" #include "format/juce_AudioFormatWriter.cpp" #include "format/juce_AudioSubsectionReader.cpp" #include "format/juce_BufferingAudioFormatReader.cpp" #include "sampler/juce_Sampler.cpp" #include "codecs/juce_AiffAudioFormat.cpp" #include "codecs/juce_CoreAudioFormat.cpp" #include "codecs/juce_FlacAudioFormat.cpp" #include "codecs/juce_MP3AudioFormat.cpp" #include "codecs/juce_OggVorbisAudioFormat.cpp" #include "codecs/juce_QuickTimeAudioFormat.cpp" #include "codecs/juce_WavAudioFormat.cpp" #include "codecs/juce_LAMEEncoderAudioFormat.cpp" #if JUCE_WINDOWS && JUCE_USE_WINDOWS_MEDIA_FORMAT #include "codecs/juce_WindowsMediaAudioFormat.cpp" #endif } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h000066400000000000000000000077711320201440200314750ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIO_FORMATS_H_INCLUDED #define JUCE_AUDIO_FORMATS_H_INCLUDED #include "../juce_audio_basics/juce_audio_basics.h" //============================================================================= /** Config: JUCE_USE_FLAC Enables the FLAC audio codec classes (available on all platforms). If your app doesn't need to read FLAC files, you might want to disable this to reduce the size of your codebase and build time. */ #ifndef JUCE_USE_FLAC #define JUCE_USE_FLAC 1 #endif /** Config: JUCE_USE_OGGVORBIS Enables the Ogg-Vorbis audio codec classes (available on all platforms). If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to reduce the size of your codebase and build time. */ #ifndef JUCE_USE_OGGVORBIS #define JUCE_USE_OGGVORBIS 1 #endif /** Config: JUCE_USE_MP3AUDIOFORMAT Enables the software-based MP3AudioFormat class. IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing that ROLI Ltd. is in no way responsible for any patent, copyright, or other legal issues that you may suffer as a result. The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party intellectual property. If you wish to use it, please seek your own independent advice about the legality of doing so. If you are not willing to accept full responsibility for the consequences of using this code, then do not enable this setting. */ #ifndef JUCE_USE_MP3AUDIOFORMAT #define JUCE_USE_MP3AUDIOFORMAT 0 #endif /** Config: JUCE_USE_LAME_AUDIO_FORMAT Enables the LameEncoderAudioFormat class. */ #ifndef JUCE_USE_LAME_AUDIO_FORMAT #define JUCE_USE_LAME_AUDIO_FORMAT 0 #endif /** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT Enables the Windows Media SDK codecs. */ #ifndef JUCE_USE_WINDOWS_MEDIA_FORMAT #define JUCE_USE_WINDOWS_MEDIA_FORMAT 1 #endif #if ! JUCE_MSVC #undef JUCE_USE_WINDOWS_MEDIA_FORMAT #define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 #endif //============================================================================= namespace juce { class AudioFormat; #include "format/juce_AudioFormatReader.h" #include "format/juce_AudioFormatWriter.h" #include "format/juce_MemoryMappedAudioFormatReader.h" #include "format/juce_AudioFormat.h" #include "format/juce_AudioFormatManager.h" #include "format/juce_AudioFormatReaderSource.h" #include "format/juce_AudioSubsectionReader.h" #include "format/juce_BufferingAudioFormatReader.h" #include "codecs/juce_AiffAudioFormat.h" #include "codecs/juce_CoreAudioFormat.h" #include "codecs/juce_FlacAudioFormat.h" #include "codecs/juce_LAMEEncoderAudioFormat.h" #include "codecs/juce_MP3AudioFormat.h" #include "codecs/juce_OggVorbisAudioFormat.h" #include "codecs/juce_QuickTimeAudioFormat.h" #include "codecs/juce_WavAudioFormat.h" #include "codecs/juce_WindowsMediaAudioFormat.h" #include "sampler/juce_Sampler.h" } #endif // JUCE_AUDIO_FORMATS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm000066400000000000000000000017061320201440200316470ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_audio_formats.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/juce_module_info000066400000000000000000000014731320201440200307040ustar00rootroot00000000000000{ "id": "juce_audio_formats", "name": "JUCE audio file format codecs", "version": "3.2.0", "description": "Classes for reading and writing various audio file formats.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", "dependencies": [ { "id": "juce_audio_basics", "version": "matching" } ], "include": "juce_audio_formats.h", "compile": [ { "file": "juce_audio_formats.cpp", "target": "! xcode" }, { "file": "juce_audio_formats.mm", "target": "xcode" } ], "browse": [ "format/*", "codecs/*", "sampler/*" ], "OSXFrameworks": "CoreAudio CoreMIDI QuartzCore AudioToolbox", "iOSFrameworks": "AudioToolbox QuartzCore" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/sampler/000077500000000000000000000000001320201440200271115ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp000066400000000000000000000154241320201440200322340ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ SamplerSound::SamplerSound (const String& soundName, AudioFormatReader& source, const BigInteger& notes, const int midiNoteForNormalPitch, const double attackTimeSecs, const double releaseTimeSecs, const double maxSampleLengthSeconds) : name (soundName), midiNotes (notes), midiRootNote (midiNoteForNormalPitch) { sourceSampleRate = source.sampleRate; if (sourceSampleRate <= 0 || source.lengthInSamples <= 0) { length = 0; attackSamples = 0; releaseSamples = 0; } else { length = jmin ((int) source.lengthInSamples, (int) (maxSampleLengthSeconds * sourceSampleRate)); data = new AudioSampleBuffer (jmin (2, (int) source.numChannels), length + 4); source.read (data, 0, length + 4, 0, true, true); attackSamples = roundToInt (attackTimeSecs * sourceSampleRate); releaseSamples = roundToInt (releaseTimeSecs * sourceSampleRate); } } SamplerSound::~SamplerSound() { } bool SamplerSound::appliesToNote (int midiNoteNumber) { return midiNotes [midiNoteNumber]; } bool SamplerSound::appliesToChannel (int /*midiChannel*/) { return true; } //============================================================================== SamplerVoice::SamplerVoice() : pitchRatio (0.0), sourceSamplePosition (0.0), lgain (0.0f), rgain (0.0f), attackReleaseLevel (0), attackDelta (0), releaseDelta (0), isInAttack (false), isInRelease (false) { } SamplerVoice::~SamplerVoice() { } bool SamplerVoice::canPlaySound (SynthesiserSound* sound) { return dynamic_cast (sound) != nullptr; } void SamplerVoice::startNote (const int midiNoteNumber, const float velocity, SynthesiserSound* s, const int /*currentPitchWheelPosition*/) { if (const SamplerSound* const sound = dynamic_cast (s)) { pitchRatio = pow (2.0, (midiNoteNumber - sound->midiRootNote) / 12.0) * sound->sourceSampleRate / getSampleRate(); sourceSamplePosition = 0.0; lgain = velocity; rgain = velocity; isInAttack = (sound->attackSamples > 0); isInRelease = false; if (isInAttack) { attackReleaseLevel = 0.0f; attackDelta = (float) (pitchRatio / sound->attackSamples); } else { attackReleaseLevel = 1.0f; attackDelta = 0.0f; } if (sound->releaseSamples > 0) releaseDelta = (float) (-pitchRatio / sound->releaseSamples); else releaseDelta = -1.0f; } else { jassertfalse; // this object can only play SamplerSounds! } } void SamplerVoice::stopNote (float /*velocity*/, bool allowTailOff) { if (allowTailOff) { isInAttack = false; isInRelease = true; } else { clearCurrentNote(); } } void SamplerVoice::pitchWheelMoved (const int /*newValue*/) { } void SamplerVoice::controllerMoved (const int /*controllerNumber*/, const int /*newValue*/) { } //============================================================================== void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) { if (const SamplerSound* const playingSound = static_cast (getCurrentlyPlayingSound().get())) { const float* const inL = playingSound->data->getReadPointer (0); const float* const inR = playingSound->data->getNumChannels() > 1 ? playingSound->data->getReadPointer (1) : nullptr; float* outL = outputBuffer.getWritePointer (0, startSample); float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample) : nullptr; while (--numSamples >= 0) { const int pos = (int) sourceSamplePosition; const float alpha = (float) (sourceSamplePosition - pos); const float invAlpha = 1.0f - alpha; // just using a very simple linear interpolation here.. float l = (inL [pos] * invAlpha + inL [pos + 1] * alpha); float r = (inR != nullptr) ? (inR [pos] * invAlpha + inR [pos + 1] * alpha) : l; l *= lgain; r *= rgain; if (isInAttack) { l *= attackReleaseLevel; r *= attackReleaseLevel; attackReleaseLevel += attackDelta; if (attackReleaseLevel >= 1.0f) { attackReleaseLevel = 1.0f; isInAttack = false; } } else if (isInRelease) { l *= attackReleaseLevel; r *= attackReleaseLevel; attackReleaseLevel += releaseDelta; if (attackReleaseLevel <= 0.0f) { stopNote (0.0f, false); break; } } if (outR != nullptr) { *outL++ += l; *outR++ += r; } else { *outL++ += (l + r) * 0.5f; } sourceSamplePosition += pitchRatio; if (sourceSamplePosition > playingSound->length) { stopNote (0.0f, false); break; } } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h000066400000000000000000000126671320201440200317070ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_SAMPLER_H_INCLUDED #define JUCE_SAMPLER_H_INCLUDED //============================================================================== /** A subclass of SynthesiserSound that represents a sampled audio clip. This is a pretty basic sampler, and just attempts to load the whole audio stream into memory. To use it, create a Synthesiser, add some SamplerVoice objects to it, then give it some SampledSound objects to play. @see SamplerVoice, Synthesiser, SynthesiserSound */ class JUCE_API SamplerSound : public SynthesiserSound { public: //============================================================================== /** Creates a sampled sound from an audio reader. This will attempt to load the audio from the source into memory and store it in this object. @param name a name for the sample @param source the audio to load. This object can be safely deleted by the caller after this constructor returns @param midiNotes the set of midi keys that this sound should be played on. This is used by the SynthesiserSound::appliesToNote() method @param midiNoteForNormalPitch the midi note at which the sample should be played with its natural rate. All other notes will be pitched up or down relative to this one @param attackTimeSecs the attack (fade-in) time, in seconds @param releaseTimeSecs the decay (fade-out) time, in seconds @param maxSampleLengthSeconds a maximum length of audio to read from the audio source, in seconds */ SamplerSound (const String& name, AudioFormatReader& source, const BigInteger& midiNotes, int midiNoteForNormalPitch, double attackTimeSecs, double releaseTimeSecs, double maxSampleLengthSeconds); /** Destructor. */ ~SamplerSound(); //============================================================================== /** Returns the sample's name */ const String& getName() const noexcept { return name; } /** Returns the audio sample data. This could return nullptr if there was a problem loading the data. */ AudioSampleBuffer* getAudioData() const noexcept { return data; } //============================================================================== bool appliesToNote (int midiNoteNumber) override; bool appliesToChannel (int midiChannel) override; private: //============================================================================== friend class SamplerVoice; String name; ScopedPointer data; double sourceSampleRate; BigInteger midiNotes; int length, attackSamples, releaseSamples; int midiRootNote; JUCE_LEAK_DETECTOR (SamplerSound) }; //============================================================================== /** A subclass of SynthesiserVoice that can play a SamplerSound. To use it, create a Synthesiser, add some SamplerVoice objects to it, then give it some SampledSound objects to play. @see SamplerSound, Synthesiser, SynthesiserVoice */ class JUCE_API SamplerVoice : public SynthesiserVoice { public: //============================================================================== /** Creates a SamplerVoice. */ SamplerVoice(); /** Destructor. */ ~SamplerVoice(); //============================================================================== bool canPlaySound (SynthesiserSound*) override; void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; void stopNote (float velocity, bool allowTailOff) override; void pitchWheelMoved (int newValue) override; void controllerMoved (int controllerNumber, int newValue) override; void renderNextBlock (AudioSampleBuffer&, int startSample, int numSamples) override; private: //============================================================================== double pitchRatio; double sourceSamplePosition; float lgain, rgain, attackReleaseLevel, attackDelta, releaseDelta; bool isInAttack, isInRelease; JUCE_LEAK_DETECTOR (SamplerVoice) }; #endif // JUCE_SAMPLER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/000077500000000000000000000000001320201440200261755ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format/000077500000000000000000000000001320201440200274655ustar00rootroot00000000000000juce_AudioPluginFormat.cpp000066400000000000000000000020031320201440200345040ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioPluginFormat::AudioPluginFormat() noexcept {} AudioPluginFormat::~AudioPluginFormat() {} juce_AudioPluginFormat.h000066400000000000000000000110251320201440200341550ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPLUGINFORMAT_H_INCLUDED #define JUCE_AUDIOPLUGINFORMAT_H_INCLUDED //============================================================================== /** The base class for a type of plugin format, such as VST, AudioUnit, LADSPA, etc. @see AudioFormatManager */ class JUCE_API AudioPluginFormat { public: //============================================================================== /** Destructor. */ virtual ~AudioPluginFormat(); //============================================================================== /** Returns the format name. E.g. "VST", "AudioUnit", etc. */ virtual String getName() const = 0; /** This tries to create descriptions for all the plugin types available in a binary module file. The file will be some kind of DLL or bundle. Normally there will only be one type returned, but some plugins (e.g. VST shells) can use a single DLL to create a set of different plugin subtypes, so in that case, each subtype is returned as a separate object. */ virtual void findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) = 0; /** Tries to recreate a type from a previously generated PluginDescription. @see PluginDescription::createInstance */ virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double initialSampleRate, int initialBufferSize) = 0; /** Should do a quick check to see if this file or directory might be a plugin of this format. This is for searching for potential files, so it shouldn't actually try to load the plugin or do anything time-consuming. */ virtual bool fileMightContainThisPluginType (const String& fileOrIdentifier) = 0; /** Returns a readable version of the name of the plugin that this identifier refers to. */ virtual String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) = 0; /** Returns true if this plugin's version or date has changed and it should be re-checked. */ virtual bool pluginNeedsRescanning (const PluginDescription&) = 0; /** Checks whether this plugin could possibly be loaded. It doesn't actually need to load it, just to check whether the file or component still exists. */ virtual bool doesPluginStillExist (const PluginDescription& desc) = 0; /** Returns true if this format needs to run a scan to find its list of plugins. */ virtual bool canScanForPlugins() const = 0; /** Searches a suggested set of directories for any plugins in this format. The path might be ignored, e.g. by AUs, which are found by the OS rather than manually. */ virtual StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, bool recursive) = 0; /** Returns the typical places to look for this kind of plugin. Note that if this returns no paths, it means that the format doesn't search in files or folders, e.g. AudioUnits. */ virtual FileSearchPath getDefaultLocationsToSearch() = 0; protected: //============================================================================== AudioPluginFormat() noexcept; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormat) }; #endif // JUCE_AUDIOPLUGINFORMAT_H_INCLUDED juce_AudioPluginFormatManager.cpp000066400000000000000000000067351320201440200360170ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioPluginFormatManager::AudioPluginFormatManager() {} AudioPluginFormatManager::~AudioPluginFormatManager() {} //============================================================================== void AudioPluginFormatManager::addDefaultFormats() { #if JUCE_DEBUG // you should only call this method once! for (int i = formats.size(); --i >= 0;) { #if JUCE_PLUGINHOST_VST jassert (dynamic_cast (formats[i]) == nullptr); #endif #if JUCE_PLUGINHOST_VST3 jassert (dynamic_cast (formats[i]) == nullptr); #endif #if JUCE_PLUGINHOST_AU && JUCE_MAC jassert (dynamic_cast (formats[i]) == nullptr); #endif #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX jassert (dynamic_cast (formats[i]) == nullptr); #endif } #endif #if JUCE_PLUGINHOST_AU && JUCE_MAC formats.add (new AudioUnitPluginFormat()); #endif #if JUCE_PLUGINHOST_VST formats.add (new VSTPluginFormat()); #endif #if JUCE_PLUGINHOST_VST3 formats.add (new VST3PluginFormat()); #endif #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX formats.add (new LADSPAPluginFormat()); #endif } int AudioPluginFormatManager::getNumFormats() { return formats.size(); } AudioPluginFormat* AudioPluginFormatManager::getFormat (const int index) { return formats [index]; } void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format) { formats.add (format); } AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, double rate, int blockSize, String& errorMessage) const { for (int i = 0; i < formats.size(); ++i) if (AudioPluginInstance* result = formats.getUnchecked(i)->createInstanceFromDescription (description, rate, blockSize)) return result; errorMessage = doesPluginStillExist (description) ? TRANS ("This plug-in failed to load correctly") : TRANS ("This plug-in file no longer exists"); return nullptr; } bool AudioPluginFormatManager::doesPluginStillExist (const PluginDescription& description) const { for (int i = 0; i < formats.size(); ++i) if (formats.getUnchecked(i)->getName() == description.pluginFormatName) return formats.getUnchecked(i)->doesPluginStillExist (description); return false; } juce_AudioPluginFormatManager.h000066400000000000000000000066251320201440200354620ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED #define JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED //============================================================================== /** This maintains a list of known AudioPluginFormats. @see AudioPluginFormat */ class JUCE_API AudioPluginFormatManager { public: //============================================================================== AudioPluginFormatManager(); /** Destructor. */ ~AudioPluginFormatManager(); //============================================================================== /** Adds any formats that it knows about, e.g. VST. */ void addDefaultFormats(); //============================================================================== /** Returns the number of types of format that are available. Use getFormat() to get one of them. */ int getNumFormats(); /** Returns one of the available formats. @see getNumFormats */ AudioPluginFormat* getFormat (int index); //============================================================================== /** Adds a format to the list. The object passed in will be owned and deleted by the manager. */ void addFormat (AudioPluginFormat* format); //============================================================================== /** Tries to load the type for this description, by trying all the formats that this manager knows about. The caller is responsible for deleting the object that is returned. If it can't load the plugin, it returns nullptr and leaves a message in the errorMessage string. */ AudioPluginInstance* createPluginInstance (const PluginDescription& description, double initialSampleRate, int initialBufferSize, String& errorMessage) const; /** Checks that the file or component for this plugin actually still exists. (This won't try to load the plugin) */ bool doesPluginStillExist (const PluginDescription& description) const; private: //============================================================================== OwnedArray formats; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager) }; #endif // JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/000077500000000000000000000000001320201440200307115ustar00rootroot00000000000000juce_AudioUnitPluginFormat.h000066400000000000000000000046051320201440200362470ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if (JUCE_PLUGINHOST_AU && JUCE_MAC) || DOXYGEN //============================================================================== /** Implements a plugin format manager for AudioUnits. */ class JUCE_API AudioUnitPluginFormat : public AudioPluginFormat { public: //============================================================================== AudioUnitPluginFormat(); ~AudioUnitPluginFormat(); //============================================================================== String getName() const override { return "AudioUnit"; } void findAllTypesForFile (OwnedArray&, const String& fileOrIdentifier) override; AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override; bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; bool pluginNeedsRescanning (const PluginDescription&) override; StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive); bool doesPluginStillExist (const PluginDescription&) override; FileSearchPath getDefaultLocationsToSearch() override; bool canScanForPlugins() const override { return true; } private: //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginFormat) }; #endif juce_AudioUnitPluginFormat.mm000066400000000000000000001761051320201440200364360ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_PLUGINHOST_AU && JUCE_MAC } // (juce namespace) #include #include #include #include #include #if JUCE_SUPPORT_CARBON #include #endif namespace juce { #include "../../juce_audio_devices/native/juce_MidiDataConcatenator.h" #if JUCE_SUPPORT_CARBON #include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h" #endif // Change this to disable logging of various activities #ifndef AU_LOGGING #define AU_LOGGING 1 #endif #if AU_LOGGING #define JUCE_AU_LOG(a) Logger::writeToLog(a); #else #define JUCE_AU_LOG(a) #endif namespace AudioUnitFormatHelpers { #if JUCE_DEBUG static ThreadLocalValue insideCallback; #endif String osTypeToString (OSType type) noexcept { const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff), (juce_wchar) ((type >> 16) & 0xff), (juce_wchar) ((type >> 8) & 0xff), (juce_wchar) (type & 0xff) }; return String (s, 4); } OSType stringToOSType (String s) { if (s.trim().length() >= 4) // (to avoid trimming leading spaces) s = s.trim(); s += " "; return (((OSType) (unsigned char) s[0]) << 24) | (((OSType) (unsigned char) s[1]) << 16) | (((OSType) (unsigned char) s[2]) << 8) | ((OSType) (unsigned char) s[3]); } static const char* auIdentifierPrefix = "AudioUnit:"; String createPluginIdentifier (const AudioComponentDescription& desc) { String s (auIdentifierPrefix); if (desc.componentType == kAudioUnitType_MusicDevice) s << "Synths/"; else if (desc.componentType == kAudioUnitType_MusicEffect || desc.componentType == kAudioUnitType_Effect) s << "Effects/"; else if (desc.componentType == kAudioUnitType_Generator) s << "Generators/"; else if (desc.componentType == kAudioUnitType_Panner) s << "Panners/"; s << osTypeToString (desc.componentType) << "," << osTypeToString (desc.componentSubType) << "," << osTypeToString (desc.componentManufacturer); return s; } void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer) { CFStringRef cfName; if (AudioComponentCopyName (comp, &cfName) == noErr) { name = String::fromCFString (cfName); CFRelease (cfName); } if (name.containsChar (':')) { manufacturer = name.upToFirstOccurrenceOf (":", false, false).trim(); name = name.fromFirstOccurrenceOf (":", false, false).trim(); } if (name.isEmpty()) name = ""; } bool getComponentDescFromIdentifier (const String& fileOrIdentifier, AudioComponentDescription& desc, String& name, String& version, String& manufacturer) { if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix)) { String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (':'), fileOrIdentifier.lastIndexOfChar ('/')) + 1)); StringArray tokens; tokens.addTokens (s, ",", StringRef()); tokens.removeEmptyStrings(); if (tokens.size() == 3) { zerostruct (desc); desc.componentType = stringToOSType (tokens[0]); desc.componentSubType = stringToOSType (tokens[1]); desc.componentManufacturer = stringToOSType (tokens[2]); if (AudioComponent comp = AudioComponentFindNext (0, &desc)) { getNameAndManufacturer (comp, name, manufacturer); if (manufacturer.isEmpty()) manufacturer = tokens[2]; if (version.isEmpty()) { UInt32 versionNum; if (AudioComponentGetVersion (comp, &versionNum) == noErr) { version << (int) (versionNum >> 16) << "." << (int) ((versionNum >> 8) & 0xff) << "." << (int) (versionNum & 0xff); } } return true; } } } return false; } bool getComponentDescFromFile (const String& fileOrIdentifier, AudioComponentDescription& desc, String& name, String& version, String& manufacturer) { zerostruct (desc); const File file (fileOrIdentifier); if (! file.hasFileExtension (".component")) return false; const char* const utf8 = fileOrIdentifier.toUTF8(); if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, (CFIndex) strlen (utf8), file.isDirectory())) { CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); if (bundleRef != 0) { CFTypeRef bundleName = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); if (bundleName != 0 && CFGetTypeID (bundleName) == CFStringGetTypeID()) name = String::fromCFString ((CFStringRef) bundleName); if (name.isEmpty()) name = file.getFileNameWithoutExtension(); CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion")); if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID()) version = String::fromCFString ((CFStringRef) versionString); CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString")); if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) manufacturer = String::fromCFString ((CFStringRef) manuString); const short resFileId = CFBundleOpenBundleResourceMap (bundleRef); UseResFile (resFileId); const OSType thngType = stringToOSType ("thng"); for (ResourceIndex i = 1; i <= Count1Resources (thngType); ++i) { if (Handle h = Get1IndResource (thngType, i)) { HLock (h); const uint32* const types = (const uint32*) *h; if (types[0] == kAudioUnitType_MusicDevice || types[0] == kAudioUnitType_MusicEffect || types[0] == kAudioUnitType_Effect || types[0] == kAudioUnitType_Generator || types[0] == kAudioUnitType_Panner) { desc.componentType = types[0]; desc.componentSubType = types[1]; desc.componentManufacturer = types[2]; if (AudioComponent comp = AudioComponentFindNext (0, &desc)) getNameAndManufacturer (comp, name, manufacturer); break; } HUnlock (h); ReleaseResource (h); } } CFBundleCloseBundleResourceMap (bundleRef, resFileId); CFRelease (bundleRef); } } return desc.componentType != 0 && desc.componentSubType != 0; } const char* getCategory (OSType type) noexcept { switch (type) { case kAudioUnitType_Effect: case kAudioUnitType_MusicEffect: return "Effect"; case kAudioUnitType_MusicDevice: return "Synth"; case kAudioUnitType_Generator: return "Generator"; case kAudioUnitType_Panner: return "Panner"; default: break; } return nullptr; } } //============================================================================== class AudioUnitPluginWindowCarbon; class AudioUnitPluginWindowCocoa; //============================================================================== class AudioUnitPluginInstance : public AudioPluginInstance { public: AudioUnitPluginInstance (const String& fileOrId) : fileOrIdentifier (fileOrId), wantsMidiMessages (false), producesMidiMessages (false), wasPlaying (false), prepared (false), currentBuffer (nullptr), numInputBusChannels (0), numOutputBusChannels (0), numInputBusses (0), numOutputBusses (0), audioUnit (nullptr), eventListenerRef (0), midiConcatenator (2048) { using namespace AudioUnitFormatHelpers; #if JUCE_DEBUG ++*insideCallback; #endif JUCE_AU_LOG ("Opening AU: " + fileOrIdentifier); if (getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer) || getComponentDescFromFile (fileOrIdentifier, componentDesc, pluginName, version, manufacturer)) { if (AudioComponent comp = AudioComponentFindNext (0, &componentDesc)) { AudioComponentInstanceNew (comp, &audioUnit); wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice || componentDesc.componentType == kAudioUnitType_MusicEffect; } } #if JUCE_DEBUG --*insideCallback; #endif } ~AudioUnitPluginInstance() { const ScopedLock sl (lock); #if JUCE_DEBUG // this indicates that some kind of recursive call is getting triggered that's // deleting this plugin while it's still under construction. jassert (AudioUnitFormatHelpers::insideCallback.get() == 0); #endif if (eventListenerRef != 0) { AUListenerDispose (eventListenerRef); eventListenerRef = 0; } if (audioUnit != nullptr) { if (prepared) releaseResources(); AudioComponentInstanceDispose (audioUnit); audioUnit = nullptr; } } void initialise (double rate, int blockSize) { refreshParameterList(); updateNumChannels(); producesMidiMessages = canProduceMidiOutput(); setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), (int) (numOutputBusChannels * numOutputBusses), rate, blockSize); setLatencySamples (0); if (parameters.size() == 0) { // some plugins crash if initialiseAudioUnit() is called too soon (sigh..), so we'll // only call it here if it seems like they it's one of the awkward plugins that can // only create their parameters after it has been initialised. initialiseAudioUnit(); refreshParameterList(); } setPluginCallbacks(); } //============================================================================== // AudioPluginInstance methods: void fillInPluginDescription (PluginDescription& desc) const override { desc.name = pluginName; desc.descriptiveName = pluginName; desc.fileOrIdentifier = AudioUnitFormatHelpers::createPluginIdentifier (componentDesc); desc.uid = ((int) componentDesc.componentType) ^ ((int) componentDesc.componentSubType) ^ ((int) componentDesc.componentManufacturer); desc.lastFileModTime = Time(); desc.pluginFormatName = "AudioUnit"; desc.category = AudioUnitFormatHelpers::getCategory (componentDesc.componentType); desc.manufacturerName = manufacturer; desc.version = version; desc.numInputChannels = getNumInputChannels(); desc.numOutputChannels = getNumOutputChannels(); desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice); } void* getPlatformSpecificData() override { return audioUnit; } const String getName() const override { return pluginName; } bool silenceInProducesSilenceOut() const override { return getTailLengthSeconds() <= 0; } double getTailLengthSeconds() const override { Float64 tail = 0; UInt32 tailSize = sizeof (tail); if (audioUnit != nullptr) AudioUnitGetProperty (audioUnit, kAudioUnitProperty_TailTime, kAudioUnitScope_Global, 0, &tail, &tailSize); return tail; } bool acceptsMidi() const override { return wantsMidiMessages; } bool producesMidi() const override { return producesMidiMessages; } //============================================================================== // AudioProcessor methods: void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override { if (audioUnit != nullptr) { releaseResources(); updateNumChannels(); Float64 sampleRateIn = 0, sampleRateOut = 0; UInt32 sampleRateSize = sizeof (sampleRateIn); const Float64 sr = newSampleRate; for (AudioUnitElement i = 0; i < numInputBusses; ++i) { AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sampleRateIn, &sampleRateSize); if (sampleRateIn != sr) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sr, sizeof (sr)); } for (AudioUnitElement i = 0; i < numOutputBusses; ++i) { AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sampleRateOut, &sampleRateSize); if (sampleRateOut != sr) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sr, sizeof (sr)); } UInt32 frameSize = (UInt32) estimatedSamplesPerBlock; AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &frameSize, sizeof (frameSize)); setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), (int) (numOutputBusChannels * numOutputBusses), (double) newSampleRate, estimatedSamplesPerBlock); Float64 latencySecs = 0.0; UInt32 latencySize = sizeof (latencySecs); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &latencySecs, &latencySize); setLatencySamples (roundToInt (latencySecs * newSampleRate)); { AudioStreamBasicDescription stream; zerostruct (stream); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) stream.mSampleRate = sr; stream.mFormatID = kAudioFormatLinearPCM; stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian; stream.mFramesPerPacket = 1; stream.mBytesPerPacket = 4; stream.mBytesPerFrame = 4; stream.mBitsPerChannel = 32; stream.mChannelsPerFrame = numInputBusChannels; for (AudioUnitElement i = 0; i < numInputBusses; ++i) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, &stream, sizeof (stream)); stream.mChannelsPerFrame = numOutputBusChannels; for (AudioUnitElement i = 0; i < numOutputBusses; ++i) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, i, &stream, sizeof (stream)); } if (numOutputBusses != 0 && numOutputBusChannels != 0) outputBufferList.calloc (numOutputBusses, getAudioBufferSizeInBytes()); zerostruct (timeStamp); timeStamp.mSampleTime = 0; timeStamp.mHostTime = AudioGetCurrentHostTime(); timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid; currentBuffer = nullptr; wasPlaying = false; resetBusses(); jassert (! prepared); initialiseAudioUnit(); } } void releaseResources() override { if (prepared) { AudioUnitUninitialize (audioUnit); resetBusses(); AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); outputBufferList.free(); currentBuffer = nullptr; prepared = false; } incomingMidi.clear(); } bool initialiseAudioUnit() { if (! prepared) prepared = (AudioUnitInitialize (audioUnit) == noErr); return prepared; } void resetBusses() { for (AudioUnitElement i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); for (AudioUnitElement i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); } void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override { const int numSamples = buffer.getNumSamples(); if (prepared) { timeStamp.mHostTime = AudioGetCurrentHostTime(); for (AudioUnitElement i = 0; i < numOutputBusses; ++i) { if (AudioBufferList* const abl = getAudioBufferListForBus(i)) { abl->mNumberBuffers = numOutputBusChannels; for (AudioUnitElement j = 0; j < numOutputBusChannels; ++j) { abl->mBuffers[j].mNumberChannels = 1; abl->mBuffers[j].mDataByteSize = sizeof (float) * (size_t) numSamples; abl->mBuffers[j].mData = buffer.getWritePointer ((int) (i * numOutputBusChannels + j)); } } } currentBuffer = &buffer; if (wantsMidiMessages) { const uint8* midiEventData; int midiEventSize, midiEventPosition; for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (midiEventData, midiEventSize, midiEventPosition);) { if (midiEventSize <= 3) MusicDeviceMIDIEvent (audioUnit, midiEventData[0], midiEventData[1], midiEventData[2], (UInt32) midiEventPosition); else MusicDeviceSysEx (audioUnit, midiEventData, (UInt32) midiEventSize); } midiMessages.clear(); } for (AudioUnitElement i = 0; i < numOutputBusses; ++i) { AudioUnitRenderActionFlags flags = 0; AudioUnitRender (audioUnit, &flags, &timeStamp, i, (UInt32) numSamples, getAudioBufferListForBus (i)); } timeStamp.mSampleTime += numSamples; } else { // Plugin not working correctly, so just bypass.. for (int i = 0; i < getNumOutputChannels(); ++i) buffer.clear (i, 0, buffer.getNumSamples()); } if (producesMidiMessages) { const ScopedLock sl (midiInLock); midiMessages.swapWith (incomingMidi); incomingMidi.clear(); } } //============================================================================== bool hasEditor() const override { return true; } AudioProcessorEditor* createEditor() override; //============================================================================== const String getInputChannelName (int index) const override { if (isPositiveAndBelow (index, getNumInputChannels())) return "Input " + String (index + 1); return String(); } const String getOutputChannelName (int index) const override { if (isPositiveAndBelow (index, getNumOutputChannels())) return "Output " + String (index + 1); return String(); } bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumInputChannels()); } bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumOutputChannels()); } //============================================================================== int getNumParameters() override { return parameters.size(); } float getParameter (int index) override { const ScopedLock sl (lock); AudioUnitParameterValue value = 0; if (audioUnit != nullptr) { if (const ParamInfo* p = parameters[index]) { AudioUnitGetParameter (audioUnit, p->paramID, kAudioUnitScope_Global, 0, &value); value = (value - p->minValue) / (p->maxValue - p->minValue); } } return value; } void setParameter (int index, float newValue) override { const ScopedLock sl (lock); if (audioUnit != nullptr) { if (const ParamInfo* p = parameters[index]) { AudioUnitSetParameter (audioUnit, p->paramID, kAudioUnitScope_Global, 0, p->minValue + (p->maxValue - p->minValue) * newValue, 0); sendParameterChangeEvent (index); } } } void sendParameterChangeEvent (int index) { jassert (audioUnit != nullptr); const ParamInfo& p = *parameters.getUnchecked (index); AudioUnitEvent ev; ev.mEventType = kAudioUnitEvent_ParameterValueChange; ev.mArgument.mParameter.mAudioUnit = audioUnit; ev.mArgument.mParameter.mParameterID = p.paramID; ev.mArgument.mParameter.mScope = kAudioUnitScope_Global; ev.mArgument.mParameter.mElement = 0; AUEventListenerNotify (nullptr, nullptr, &ev); } void sendAllParametersChangedEvents() { jassert (audioUnit != nullptr); AudioUnitParameter param; param.mAudioUnit = audioUnit; param.mParameterID = kAUParameterListener_AnyParameter; AUParameterListenerNotify (nullptr, nullptr, ¶m); } const String getParameterName (int index) override { if (const ParamInfo* p = parameters[index]) return p->name; return String(); } const String getParameterText (int index) override { return String (getParameter (index)); } bool isParameterAutomatable (int index) const override { if (const ParamInfo* p = parameters[index]) return p->automatable; return false; } //============================================================================== int getNumPrograms() override { CFArrayRef presets; UInt32 sz = sizeof (CFArrayRef); int num = 0; if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, &presets, &sz) == noErr) { num = (int) CFArrayGetCount (presets); CFRelease (presets); } return num; } int getCurrentProgram() override { AUPreset current; current.presetNumber = 0; UInt32 sz = sizeof (AUPreset); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0, ¤t, &sz); return current.presetNumber; } void setCurrentProgram (int newIndex) override { AUPreset current; current.presetNumber = newIndex; current.presetName = CFSTR(""); AudioUnitSetProperty (audioUnit, kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0, ¤t, sizeof (AUPreset)); sendAllParametersChangedEvents(); } const String getProgramName (int index) override { String s; CFArrayRef presets; UInt32 sz = sizeof (CFArrayRef); if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, &presets, &sz) == noErr) { for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i) { if (const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i)) { if (p->presetNumber == index) { s = String::fromCFString (p->presetName); break; } } } CFRelease (presets); } return s; } void changeProgramName (int /*index*/, const String& /*newName*/) override { jassertfalse; // xxx not implemented! } //============================================================================== void getStateInformation (MemoryBlock& destData) override { getCurrentProgramStateInformation (destData); } void getCurrentProgramStateInformation (MemoryBlock& destData) override { CFPropertyListRef propertyList = 0; UInt32 sz = sizeof (CFPropertyListRef); if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &propertyList, &sz) == noErr) { CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault); CFWriteStreamOpen (stream); CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0); CFWriteStreamClose (stream); CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); destData.setSize ((size_t) bytesWritten); destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); CFRelease (data); CFRelease (stream); CFRelease (propertyList); } } void setStateInformation (const void* data, int sizeInBytes) override { setCurrentProgramStateInformation (data, sizeInBytes); } void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override { CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) data, sizeInBytes, kCFAllocatorNull); CFReadStreamOpen (stream); CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, &format, 0); CFRelease (stream); if (propertyList != 0) { initialiseAudioUnit(); AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &propertyList, sizeof (propertyList)); sendAllParametersChangedEvents(); CFRelease (propertyList); } } void refreshParameterList() override { parameters.clear(); if (audioUnit != nullptr) { UInt32 paramListSize = 0; AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, 0, ¶mListSize); if (paramListSize > 0) { const size_t numParams = paramListSize / sizeof (int); HeapBlock ids; ids.calloc (numParams); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, ids, ¶mListSize); for (int i = 0; i < numParams; ++i) { AudioUnitParameterInfo info; UInt32 sz = sizeof (info); if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, ids[i], &info, &sz) == noErr) { ParamInfo* const param = new ParamInfo(); parameters.add (param); param->paramID = ids[i]; param->minValue = info.minValue; param->maxValue = info.maxValue; param->automatable = (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) { param->name = String::fromCFString (info.cfNameString); if ((info.flags & kAudioUnitParameterFlag_CFNameRelease) != 0) CFRelease (info.cfNameString); } else { param->name = String (info.name, sizeof (info.name)); } } } } } } void handleIncomingMidiMessage (void*, const MidiMessage& message) { const ScopedLock sl (midiInLock); incomingMidi.addEvent (message, 0); } void handlePartialSysexMessage (void*, const uint8*, int, double) {} private: //============================================================================== friend class AudioUnitPluginWindowCarbon; friend class AudioUnitPluginWindowCocoa; friend class AudioUnitPluginFormat; AudioComponentDescription componentDesc; String pluginName, manufacturer, version; String fileOrIdentifier; CriticalSection lock; bool wantsMidiMessages, producesMidiMessages, wasPlaying, prepared; HeapBlock outputBufferList; AudioTimeStamp timeStamp; AudioSampleBuffer* currentBuffer; AudioUnitElement numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; AudioUnit audioUnit; AUEventListenerRef eventListenerRef; struct ParamInfo { UInt32 paramID; String name; AudioUnitParameterValue minValue, maxValue; bool automatable; }; OwnedArray parameters; MidiDataConcatenator midiConcatenator; CriticalSection midiInLock; MidiBuffer incomingMidi; void setPluginCallbacks() { if (audioUnit != nullptr) { { AURenderCallbackStruct info; zerostruct (info); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) info.inputProcRefCon = this; info.inputProc = renderGetInputCallback; for (AudioUnitElement i = 0; i < numInputBusses; ++i) AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, i, &info, sizeof (info)); } if (producesMidiMessages) { AUMIDIOutputCallbackStruct info; zerostruct (info); info.userData = this; info.midiOutputCallback = renderMidiOutputCallback; producesMidiMessages = (AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallback, kAudioUnitScope_Global, 0, &info, sizeof (info)) == noErr); } { HostCallbackInfo info; zerostruct (info); info.hostUserData = this; info.beatAndTempoProc = getBeatAndTempoCallback; info.musicalTimeLocationProc = getMusicalTimeLocationCallback; info.transportStateProc = getTransportStateCallback; AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, 0, &info, sizeof (info)); } AUEventListenerCreate (eventListenerCallback, this, #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 CFRunLoopGetMain(), #else nullptr, #endif kCFRunLoopDefaultMode, 0, 0, &eventListenerRef); for (int i = 0; i < parameters.size(); ++i) { AudioUnitEvent event; event.mArgument.mParameter.mAudioUnit = audioUnit; event.mArgument.mParameter.mParameterID = parameters.getUnchecked(i)->paramID; event.mArgument.mParameter.mScope = kAudioUnitScope_Global; event.mArgument.mParameter.mElement = 0; event.mEventType = kAudioUnitEvent_ParameterValueChange; AUEventListenerAddEventType (eventListenerRef, nullptr, &event); event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; AUEventListenerAddEventType (eventListenerRef, nullptr, &event); event.mEventType = kAudioUnitEvent_EndParameterChangeGesture; AUEventListenerAddEventType (eventListenerRef, nullptr, &event); } // Add a listener for program changes AudioUnitEvent event; event.mArgument.mProperty.mAudioUnit = audioUnit; event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_PresentPreset; event.mArgument.mProperty.mScope = kAudioUnitScope_Global; event.mArgument.mProperty.mElement = 0; event.mEventType = kAudioUnitEvent_PropertyChange; AUEventListenerAddEventType (eventListenerRef, nullptr, &event); } } void eventCallback (const AudioUnitEvent& event, AudioUnitParameterValue newValue) { switch (event.mEventType) { case kAudioUnitEvent_ParameterValueChange: for (int i = 0; i < parameters.size(); ++i) { const ParamInfo& p = *parameters.getUnchecked(i); if (p.paramID == event.mArgument.mParameter.mParameterID) { sendParamChangeMessageToListeners (i, (newValue - p.minValue) / (p.maxValue - p.minValue)); break; } } break; case kAudioUnitEvent_BeginParameterChangeGesture: beginParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); break; case kAudioUnitEvent_EndParameterChangeGesture: endParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); break; default: sendAllParametersChangedEvents(); break; } } static void eventListenerCallback (void* userRef, void*, const AudioUnitEvent* event, UInt64, AudioUnitParameterValue value) { jassert (event != nullptr); static_cast (userRef)->eventCallback (*event, value); } //============================================================================== OSStatus renderGetInput (AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) const { if (currentBuffer != nullptr) { // if this ever happens, might need to add extra handling jassert (inNumberFrames == (UInt32) currentBuffer->getNumSamples()); for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i) { const int bufferChannel = (int) (inBusNumber * numInputBusChannels + i); if (bufferChannel < currentBuffer->getNumChannels()) { memcpy (ioData->mBuffers[i].mData, currentBuffer->getReadPointer (bufferChannel), sizeof (float) * inNumberFrames); } else { zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames); } } } return noErr; } OSStatus renderMidiOutput (const MIDIPacketList* pktlist) { if (pktlist != nullptr && pktlist->numPackets) { const double time = Time::getMillisecondCounterHiRes() * 0.001; const MIDIPacket* packet = &pktlist->packet[0]; for (UInt32 i = 0; i < pktlist->numPackets; ++i) { midiConcatenator.pushMidiData (packet->data, (int) packet->length, time, (void*) nullptr, *this); packet = MIDIPacketNext (packet); } } return noErr; } template static void setIfNotNull (Type1* p, Type2 value) noexcept { if (p != nullptr) *p = value; } OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const { AudioPlayHead* const ph = getPlayHead(); AudioPlayHead::CurrentPositionInfo result; if (ph != nullptr && ph->getCurrentPosition (result)) { setIfNotNull (outCurrentBeat, result.ppqPosition); setIfNotNull (outCurrentTempo, result.bpm); } else { setIfNotNull (outCurrentBeat, 0); setIfNotNull (outCurrentTempo, 120.0); } return noErr; } OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const { AudioPlayHead* const ph = getPlayHead(); AudioPlayHead::CurrentPositionInfo result; if (ph != nullptr && ph->getCurrentPosition (result)) { setIfNotNull (outTimeSig_Numerator, (UInt32) result.timeSigNumerator); setIfNotNull (outTimeSig_Denominator, (UInt32) result.timeSigDenominator); setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); //xxx setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong } else { setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); setIfNotNull (outTimeSig_Numerator, (UInt32) 4); setIfNotNull (outTimeSig_Denominator, (UInt32) 4); setIfNotNull (outCurrentMeasureDownBeat, 0); } return noErr; } OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged, Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, Float64* outCycleStartBeat, Float64* outCycleEndBeat) { AudioPlayHead* const ph = getPlayHead(); AudioPlayHead::CurrentPositionInfo result; if (ph != nullptr && ph->getCurrentPosition (result)) { setIfNotNull (outIsPlaying, result.isPlaying); if (outTransportStateChanged != nullptr) { *outTransportStateChanged = result.isPlaying != wasPlaying; wasPlaying = result.isPlaying; } setIfNotNull (outCurrentSampleInTimeLine, result.timeInSamples); setIfNotNull (outIsCycling, false); setIfNotNull (outCycleStartBeat, 0); setIfNotNull (outCycleEndBeat, 0); } else { setIfNotNull (outIsPlaying, false); setIfNotNull (outTransportStateChanged, false); setIfNotNull (outCurrentSampleInTimeLine, 0); setIfNotNull (outIsCycling, false); setIfNotNull (outCycleStartBeat, 0); setIfNotNull (outCycleEndBeat, 0); } return noErr; } //============================================================================== static OSStatus renderGetInputCallback (void* hostRef, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { return static_cast (hostRef) ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); } static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp*, UInt32 /*midiOutNum*/, const struct MIDIPacketList* pktlist) { return static_cast (hostRef)->renderMidiOutput (pktlist); } static OSStatus getBeatAndTempoCallback (void* hostRef, Float64* outCurrentBeat, Float64* outCurrentTempo) { return static_cast (hostRef)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); } static OSStatus getMusicalTimeLocationCallback (void* hostRef, UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) { return static_cast (hostRef) ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, outTimeSig_Denominator, outCurrentMeasureDownBeat); } static OSStatus getTransportStateCallback (void* hostRef, Boolean* outIsPlaying, Boolean* outTransportStateChanged, Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, Float64* outCycleStartBeat, Float64* outCycleEndBeat) { return static_cast (hostRef) ->getTransportState (outIsPlaying, outTransportStateChanged, outCurrentSampleInTimeLine, outIsCycling, outCycleStartBeat, outCycleEndBeat); } //============================================================================== size_t getAudioBufferSizeInBytes() const noexcept { return offsetof (AudioBufferList, mBuffers) + (sizeof (AudioBuffer) * numOutputBusChannels); } AudioBufferList* getAudioBufferListForBus (AudioUnitElement busIndex) const noexcept { return addBytesToPointer (outputBufferList.getData(), getAudioBufferSizeInBytes() * busIndex); } AudioUnitElement getElementCount (AudioUnitScope scope) const noexcept { UInt32 count; UInt32 countSize = sizeof (count); if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ElementCount, scope, 0, &count, &countSize) != noErr || countSize == 0) count = 1; return count; } void updateNumChannels() { numInputBusses = getElementCount (kAudioUnitScope_Input); numOutputBusses = getElementCount (kAudioUnitScope_Output); AUChannelInfo supportedChannels [128]; UInt32 supportedChannelsSize = sizeof (supportedChannels); if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, supportedChannels, &supportedChannelsSize) == noErr && supportedChannelsSize > 0) { int explicitNumIns = 0; int explicitNumOuts = 0; int maximumNumIns = 0; int maximumNumOuts = 0; for (int i = 0; i < (int) (supportedChannelsSize / sizeof (AUChannelInfo)); ++i) { const int inChannels = (int) supportedChannels[i].inChannels; const int outChannels = (int) supportedChannels[i].outChannels; if (inChannels < 0) maximumNumIns = jmin (maximumNumIns, inChannels); else explicitNumIns = jmax (explicitNumIns, inChannels); if (outChannels < 0) maximumNumOuts = jmin (maximumNumOuts, outChannels); else explicitNumOuts = jmax (explicitNumOuts, outChannels); } if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match) || (maximumNumIns == -2 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, even if they don't match) || (maximumNumIns == -1 && maximumNumOuts == -2)) { numInputBusChannels = numOutputBusChannels = 2; } else { numInputBusChannels = (AudioUnitElement) explicitNumIns; numOutputBusChannels = (AudioUnitElement) explicitNumOuts; if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns)) numInputBusChannels = 2; if (maximumNumOuts == -1 || (maximumNumOuts < 0 && explicitNumOuts <= -maximumNumOuts)) numOutputBusChannels = 2; } } else { // (this really means the plugin will take any number of ins/outs as long // as they are the same) numInputBusChannels = numOutputBusChannels = 2; } } bool canProduceMidiOutput() { UInt32 dataSize = 0; Boolean isWritable = false; if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo, kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr && dataSize != 0) { CFArrayRef midiArray; if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo, kAudioUnitScope_Global, 0, &midiArray, &dataSize) == noErr) { bool result = (CFArrayGetCount (midiArray) > 0); CFRelease (midiArray); return result; } } return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance) }; //============================================================================== class AudioUnitPluginWindowCocoa : public AudioProcessorEditor { public: AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& p, bool createGenericViewIfNeeded) : AudioProcessorEditor (&p), plugin (p) { addAndMakeVisible (wrapper); setOpaque (true); setVisible (true); setSize (100, 100); createView (createGenericViewIfNeeded); } ~AudioUnitPluginWindowCocoa() { if (isValid()) { wrapper.setVisible (false); removeChildComponent (&wrapper); wrapper.setView (nil); plugin.editorBeingDeleted (this); } } bool isValid() const { return wrapper.getView() != nil; } void paint (Graphics& g) override { g.fillAll (Colours::white); } void resized() override { wrapper.setSize (getWidth(), getHeight()); } void childBoundsChanged (Component*) override { setSize (wrapper.getWidth(), wrapper.getHeight()); } private: AudioUnitPluginInstance& plugin; AutoResizingNSViewComponent wrapper; bool createView (const bool createGenericViewIfNeeded) { if (! plugin.initialiseAudioUnit()) return false; NSView* pluginView = nil; UInt32 dataSize = 0; Boolean isWritable = false; if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr && dataSize != 0 && AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr) { HeapBlock info; info.calloc (dataSize, 1); if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, info, &dataSize) == noErr) { NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]); CFStringRef path = CFURLCopyPath (info->mCocoaAUViewBundleLocation); NSString* unescapedPath = (NSString*) CFURLCreateStringByReplacingPercentEscapes (0, path, CFSTR ("")); CFRelease (path); NSBundle* viewBundle = [NSBundle bundleWithPath: [unescapedPath autorelease]]; Class viewClass = [viewBundle classNamed: viewClassName]; if ([viewClass conformsToProtocol: @protocol (AUCocoaUIBase)] && [viewClass instancesRespondToSelector: @selector (interfaceVersion)] && [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)]) { id factory = [[[viewClass alloc] init] autorelease]; pluginView = [factory uiViewForAudioUnit: plugin.audioUnit withSize: NSMakeSize (getWidth(), getHeight())]; } for (int i = (dataSize - sizeof (CFURLRef)) / sizeof (CFStringRef); --i >= 0;) CFRelease (info->mCocoaAUViewClass[i]); CFRelease (info->mCocoaAUViewBundleLocation); } } if (createGenericViewIfNeeded && (pluginView == nil)) { { // This forces CoreAudio.component to be loaded, otherwise the AUGenericView will assert AudioComponentDescription desc; String name, version, manufacturer; AudioUnitFormatHelpers::getComponentDescFromIdentifier ("AudioUnit:Output/auou,genr,appl", desc, name, version, manufacturer); } pluginView = [[AUGenericView alloc] initWithAudioUnit: plugin.audioUnit]; } wrapper.setView (pluginView); if (pluginView != nil) wrapper.resizeToFitView(); return pluginView != nil; } }; #if JUCE_SUPPORT_CARBON //============================================================================== class AudioUnitPluginWindowCarbon : public AudioProcessorEditor { public: AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& p) : AudioProcessorEditor (&p), plugin (p), audioComponent (nullptr), viewComponent (nullptr) { addAndMakeVisible (innerWrapper = new InnerWrapperComponent (*this)); setOpaque (true); setVisible (true); setSize (400, 300); UInt32 propertySize; if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &propertySize, NULL) == noErr && propertySize > 0) { HeapBlock views (propertySize / sizeof (AudioComponentDescription)); if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &views[0], &propertySize) == noErr) { audioComponent = AudioComponentFindNext (nullptr, &views[0]); } } } ~AudioUnitPluginWindowCarbon() { innerWrapper = nullptr; if (isValid()) plugin.editorBeingDeleted (this); } bool isValid() const noexcept { return audioComponent != nullptr; } //============================================================================== void paint (Graphics& g) override { g.fillAll (Colours::black); } void resized() override { if (innerWrapper != nullptr) innerWrapper->setSize (getWidth(), getHeight()); } //============================================================================== bool keyStateChanged (bool) override { return false; } bool keyPressed (const KeyPress&) override { return false; } //============================================================================== AudioUnit getAudioUnit() const { return plugin.audioUnit; } AudioUnitCarbonView getViewComponent() { if (viewComponent == nullptr && audioComponent != nullptr) AudioComponentInstanceNew (audioComponent, &viewComponent); return viewComponent; } void closeViewComponent() { if (viewComponent != nullptr) { JUCE_AU_LOG ("Closing AU GUI: " + plugin.getName()); AudioComponentInstanceDispose (viewComponent); viewComponent = nullptr; } } private: //============================================================================== AudioUnitPluginInstance& plugin; AudioComponent audioComponent; AudioUnitCarbonView viewComponent; //============================================================================== class InnerWrapperComponent : public CarbonViewWrapperComponent { public: InnerWrapperComponent (AudioUnitPluginWindowCarbon& w) : owner (w) {} ~InnerWrapperComponent() { deleteWindow(); } HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) override { JUCE_AU_LOG ("Opening AU GUI: " + owner.plugin.getName()); AudioUnitCarbonView carbonView = owner.getViewComponent(); if (carbonView == 0) return 0; Float32Point pos = { 0, 0 }; Float32Point size = { 250, 200 }; HIViewRef pluginView = 0; AudioUnitCarbonViewCreate (carbonView, owner.getAudioUnit(), windowRef, rootView, &pos, &size, (ControlRef*) &pluginView); return pluginView; } void removeView (HIViewRef) override { owner.closeViewComponent(); } private: AudioUnitPluginWindowCarbon& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InnerWrapperComponent) }; friend class InnerWrapperComponent; ScopedPointer innerWrapper; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginWindowCarbon) }; #endif //============================================================================== AudioProcessorEditor* AudioUnitPluginInstance::createEditor() { ScopedPointer w (new AudioUnitPluginWindowCocoa (*this, false)); if (! static_cast (w.get())->isValid()) w = nullptr; #if JUCE_SUPPORT_CARBON if (w == nullptr) { w = new AudioUnitPluginWindowCarbon (*this); if (! static_cast (w.get())->isValid()) w = nullptr; } #endif if (w == nullptr) w = new AudioUnitPluginWindowCocoa (*this, true); // use AUGenericView as a fallback return w.release(); } //============================================================================== //============================================================================== AudioUnitPluginFormat::AudioUnitPluginFormat() { } AudioUnitPluginFormat::~AudioUnitPluginFormat() { } void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) { if (! fileMightContainThisPluginType (fileOrIdentifier)) return; PluginDescription desc; desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; try { ScopedPointer createdInstance (createInstanceFromDescription (desc, 44100.0, 512)); if (AudioUnitPluginInstance* auInstance = dynamic_cast (createdInstance.get())) results.add (new PluginDescription (auInstance->getPluginDescription())); } catch (...) { // crashed while loading... } } AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc, double rate, int blockSize) { if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { ScopedPointer result (new AudioUnitPluginInstance (desc.fileOrIdentifier)); if (result->audioUnit != nullptr) { result->initialise (rate, blockSize); return result.release(); } } return nullptr; } StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath&, bool /*recursive*/) { StringArray result; AudioComponent comp = nullptr; for (;;) { AudioComponentDescription desc; zerostruct (desc); comp = AudioComponentFindNext (comp, &desc); if (comp == nullptr) break; AudioComponentGetDescription (comp, &desc); if (desc.componentType == kAudioUnitType_MusicDevice || desc.componentType == kAudioUnitType_MusicEffect || desc.componentType == kAudioUnitType_Effect || desc.componentType == kAudioUnitType_Generator || desc.componentType == kAudioUnitType_Panner) { result.add (AudioUnitFormatHelpers::createPluginIdentifier (desc)); } } return result; } bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { AudioComponentDescription desc; String name, version, manufacturer; if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer)) return AudioComponentFindNext (nullptr, &desc) != nullptr; const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); return f.hasFileExtension (".component") && f.isDirectory(); } String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { AudioComponentDescription desc; String name, version, manufacturer; AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer); if (name.isEmpty()) name = fileOrIdentifier; return name; } bool AudioUnitPluginFormat::pluginNeedsRescanning (const PluginDescription& desc) { AudioComponentDescription newDesc; String name, version, manufacturer; return ! (AudioUnitFormatHelpers::getComponentDescFromIdentifier (desc.fileOrIdentifier, newDesc, name, version, manufacturer) && version == desc.version); } bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) { if (desc.fileOrIdentifier.startsWithIgnoreCase (AudioUnitFormatHelpers::auIdentifierPrefix)) return fileMightContainThisPluginType (desc.fileOrIdentifier); return File (desc.fileOrIdentifier).exists(); } FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() { return FileSearchPath(); } #undef JUCE_AU_LOG #endif juce_LADSPAPluginFormat.cpp000066400000000000000000000551731320201440200356530ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX } // (juce namespace) #include namespace juce { static int shellLADSPAUIDToCreate = 0; static int insideLADSPACallback = 0; #define JUCE_LADSPA_LOGGING 1 #if JUCE_LADSPA_LOGGING #define JUCE_LADSPA_LOG(x) Logger::writeToLog (x); #else #define JUCE_LADSPA_LOG(x) #endif //============================================================================== class LADSPAModuleHandle : public ReferenceCountedObject { public: LADSPAModuleHandle (const File& f) : file (f), moduleMain (nullptr) { getActiveModules().add (this); } ~LADSPAModuleHandle() { getActiveModules().removeFirstMatchingValue (this); close(); } typedef ReferenceCountedObjectPtr Ptr; static Array & getActiveModules() { static Array activeModules; return activeModules; } static LADSPAModuleHandle* findOrCreateModule (const File& file) { for (int i = getActiveModules().size(); --i >= 0;) { LADSPAModuleHandle* const module = getActiveModules().getUnchecked(i); if (module->file == file) return module; } ++insideLADSPACallback; shellLADSPAUIDToCreate = 0; JUCE_LADSPA_LOG ("Loading LADSPA module: " + file.getFullPathName()); ScopedPointer m (new LADSPAModuleHandle (file)); if (! m->open()) m = nullptr; --insideLADSPACallback; return m.release(); } File file; LADSPA_Descriptor_Function moduleMain; private: DynamicLibrary module; bool open() { module.open (file.getFullPathName()); moduleMain = (LADSPA_Descriptor_Function) module.getFunction ("ladspa_descriptor"); return moduleMain != nullptr; } void close() { module.close(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAModuleHandle) }; //============================================================================== class LADSPAPluginInstance : public AudioPluginInstance { public: LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m) : module (m), plugin (nullptr), handle (nullptr), initialised (false), tempBuffer (1, 1) { ++insideLADSPACallback; name = module->file.getFileNameWithoutExtension(); JUCE_LADSPA_LOG ("Creating LADSPA instance: " + name); if (module->moduleMain != nullptr) { plugin = module->moduleMain (shellLADSPAUIDToCreate); if (plugin == nullptr) { JUCE_LADSPA_LOG ("Cannot find any valid descriptor in shared library"); --insideLADSPACallback; return; } } else { JUCE_LADSPA_LOG ("Cannot find any valid plugin in shared library"); --insideLADSPACallback; return; } const double sampleRate = getSampleRate() > 0 ? getSampleRate() : 44100.0; handle = plugin->instantiate (plugin, (uint32) sampleRate); --insideLADSPACallback; } ~LADSPAPluginInstance() { const ScopedLock sl (lock); jassert (insideLADSPACallback == 0); if (handle != nullptr && plugin != nullptr && plugin->cleanup != nullptr) plugin->cleanup (handle); initialised = false; module = nullptr; plugin = nullptr; handle = nullptr; } void initialise (double initialSampleRate, int initialBlockSize) { setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize); if (initialised || plugin == nullptr || handle == nullptr) return; JUCE_LADSPA_LOG ("Initialising LADSPA: " + name); initialised = true; inputs.clear(); outputs.clear(); parameters.clear(); for (unsigned int i = 0; i < plugin->PortCount; ++i) { const LADSPA_PortDescriptor portDesc = plugin->PortDescriptors[i]; if ((portDesc & LADSPA_PORT_CONTROL) != 0) parameters.add (i); if ((portDesc & LADSPA_PORT_AUDIO) != 0) { if ((portDesc & LADSPA_PORT_INPUT) != 0) inputs.add (i); if ((portDesc & LADSPA_PORT_OUTPUT) != 0) outputs.add (i); } } parameterValues.calloc (parameters.size()); for (int i = 0; i < parameters.size(); ++i) plugin->connect_port (handle, parameters[i], &(parameterValues[i].scaled)); setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize); setCurrentProgram (0); setLatencySamples (0); // Some plugins crash if this doesn't happen: if (plugin->activate != nullptr) plugin->activate (handle); if (plugin->deactivate != nullptr) plugin->deactivate (handle); } //============================================================================== // AudioPluginInstance methods: void fillInPluginDescription (PluginDescription& desc) const { desc.name = getName(); desc.fileOrIdentifier = module->file.getFullPathName(); desc.uid = getUID(); desc.lastFileModTime = module->file.getLastModificationTime(); desc.pluginFormatName = "LADSPA"; desc.category = getCategory(); desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String(); desc.version = getVersion(); desc.numInputChannels = getNumInputChannels(); desc.numOutputChannels = getNumOutputChannels(); desc.isInstrument = false; } const String getName() const { if (plugin != nullptr && plugin->Label != nullptr) return plugin->Label; return name; } int getUID() const { if (plugin != nullptr && plugin->UniqueID != 0) return (int) plugin->UniqueID; return module->file.hashCode(); } String getVersion() const { return LADSPA_VERSION; } String getCategory() const { return "Effect"; } bool acceptsMidi() const { return false; } bool producesMidi() const { return false; } bool silenceInProducesSilenceOut() const { return plugin == nullptr; } // ..any way to get a proper answer for these? double getTailLengthSeconds() const { return 0.0; } //============================================================================== void prepareToPlay (double newSampleRate, int samplesPerBlockExpected) { setLatencySamples (0); initialise (newSampleRate, samplesPerBlockExpected); if (initialised) { tempBuffer.setSize (jmax (1, outputs.size()), samplesPerBlockExpected); // dodgy hack to force some plugins to initialise the sample rate.. if (getNumParameters() > 0) { const float old = getParameter (0); setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); setParameter (0, old); } if (plugin->activate != nullptr) plugin->activate (handle); } } void releaseResources() { if (handle != nullptr && plugin->deactivate != nullptr) plugin->deactivate (handle); tempBuffer.setSize (1, 1); } void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { const int numSamples = buffer.getNumSamples(); if (initialised && plugin != nullptr && handle != nullptr) { for (int i = 0; i < inputs.size(); ++i) plugin->connect_port (handle, inputs[i], i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr); if (plugin->run != nullptr) { for (int i = 0; i < outputs.size(); ++i) plugin->connect_port (handle, outputs.getUnchecked(i), i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr); plugin->run (handle, numSamples); return; } if (plugin->run_adding != nullptr) { tempBuffer.setSize (outputs.size(), numSamples); tempBuffer.clear(); for (int i = 0; i < outputs.size(); ++i) plugin->connect_port (handle, outputs.getUnchecked(i), tempBuffer.getWritePointer (i)); plugin->run_adding (handle, numSamples); for (int i = 0; i < outputs.size(); ++i) if (i < buffer.getNumChannels()) buffer.copyFrom (i, 0, tempBuffer, i, 0, numSamples); return; } jassertfalse; // no callback to use? } for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) buffer.clear (i, 0, numSamples); } bool isInputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); } bool isOutputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); } const String getInputChannelName (const int index) const { if (isPositiveAndBelow (index, getNumInputChannels())) return String (plugin->PortNames [inputs [index]]).trim(); return String(); } const String getOutputChannelName (const int index) const { if (isPositiveAndBelow (index, getNumInputChannels())) return String (plugin->PortNames [outputs [index]]).trim(); return String(); } //============================================================================== int getNumParameters() { return handle != nullptr ? parameters.size() : 0; } bool isParameterAutomatable (int index) const { return plugin != nullptr && (plugin->PortDescriptors [parameters[index]] & LADSPA_PORT_INPUT) != 0; } float getParameter (int index) { if (plugin != nullptr && isPositiveAndBelow (index, parameters.size())) { const ScopedLock sl (lock); return parameterValues[index].unscaled; } return 0.0f; } void setParameter (int index, float newValue) { if (plugin != nullptr && isPositiveAndBelow (index, parameters.size())) { const ScopedLock sl (lock); ParameterValue& p = parameterValues[index]; if (p.unscaled != newValue) p = ParameterValue (getNewParamScaled (plugin->PortRangeHints [parameters[index]], newValue), newValue); } } const String getParameterName (int index) { if (plugin != nullptr) { jassert (isPositiveAndBelow (index, parameters.size())); return String (plugin->PortNames [parameters [index]]).trim(); } return String(); } const String getParameterText (int index) { if (plugin != nullptr) { jassert (index >= 0 && index < parameters.size()); const LADSPA_PortRangeHint& hint = plugin->PortRangeHints [parameters [index]]; if (LADSPA_IS_HINT_INTEGER (hint.HintDescriptor)) return String ((int) parameterValues[index].scaled); return String (parameterValues[index].scaled, 4); } return String(); } //============================================================================== int getNumPrograms() { return 0; } int getCurrentProgram() { return 0; } void setCurrentProgram (int newIndex) { if (plugin != nullptr) for (int i = 0; i < parameters.size(); ++i) parameterValues[i] = getParamValue (plugin->PortRangeHints [parameters[i]]); } const String getProgramName (int index) { // XXX return String(); } void changeProgramName (int index, const String& newName) { // XXX } //============================================================================== void getStateInformation (MemoryBlock& destData) { destData.setSize (sizeof (float) * getNumParameters()); destData.fillWith (0); float* const p = (float*) ((char*) destData.getData()); for (int i = 0; i < getNumParameters(); ++i) p[i] = getParameter(i); } void getCurrentProgramStateInformation (MemoryBlock& destData) { getStateInformation (destData); } void setStateInformation (const void* data, int sizeInBytes) { const float* p = static_cast (data); for (int i = 0; i < getNumParameters(); ++i) setParameter (i, p[i]); } void setCurrentProgramStateInformation (const void* data, int sizeInBytes) { setStateInformation (data, sizeInBytes); } bool hasEditor() const { return false; } AudioProcessorEditor* createEditor() { return nullptr; } bool isValid() const { return handle != nullptr; } LADSPAModuleHandle::Ptr module; const LADSPA_Descriptor* plugin; private: LADSPA_Handle handle; String name; CriticalSection lock; bool initialised; AudioSampleBuffer tempBuffer; Array inputs, outputs, parameters; struct ParameterValue { inline ParameterValue() noexcept : scaled (0), unscaled (0) {} inline ParameterValue (float s, float u) noexcept : scaled (s), unscaled (u) {} float scaled, unscaled; }; HeapBlock parameterValues; //============================================================================== static float scaledValue (float low, float high, float alpha, bool useLog) noexcept { if (useLog && low > 0 && high > 0) return expf (logf (low) * (1.0f - alpha) + logf (high) * alpha); return low + (high - low) * alpha; } static float toIntIfNecessary (const LADSPA_PortRangeHintDescriptor& desc, float value) { return LADSPA_IS_HINT_INTEGER (desc) ? ((float) (int) value) : value; } float getNewParamScaled (const LADSPA_PortRangeHint& hint, float newValue) const { const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor; if (LADSPA_IS_HINT_TOGGLED (desc)) return (newValue < 0.5f) ? 0.0f : 1.0f; const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f; const float lower = hint.LowerBound * scale; const float upper = hint.UpperBound * scale; if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_BOUNDED_ABOVE (desc)) return toIntIfNecessary (desc, scaledValue (lower, upper, newValue, LADSPA_IS_HINT_LOGARITHMIC (desc))); if (LADSPA_IS_HINT_BOUNDED_BELOW (desc)) return toIntIfNecessary (desc, newValue); if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc)) return toIntIfNecessary (desc, newValue * upper); return 0.0f; } ParameterValue getParamValue (const LADSPA_PortRangeHint& hint) const { const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor; if (LADSPA_IS_HINT_HAS_DEFAULT (desc)) { if (LADSPA_IS_HINT_DEFAULT_0 (desc)) return ParameterValue(); if (LADSPA_IS_HINT_DEFAULT_1 (desc)) return ParameterValue (1.0f, 1.0f); if (LADSPA_IS_HINT_DEFAULT_100 (desc)) return ParameterValue (100.0f, 0.5f); if (LADSPA_IS_HINT_DEFAULT_440 (desc)) return ParameterValue (440.0f, 0.5f); const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f; const float lower = hint.LowerBound * scale; const float upper = hint.UpperBound * scale; if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_DEFAULT_MINIMUM (desc)) return ParameterValue (lower, 0.0f); if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc) && LADSPA_IS_HINT_DEFAULT_MAXIMUM (desc)) return ParameterValue (upper, 1.0f); if (LADSPA_IS_HINT_BOUNDED_BELOW (desc)) { const bool useLog = LADSPA_IS_HINT_LOGARITHMIC (desc); if (LADSPA_IS_HINT_DEFAULT_LOW (desc)) return ParameterValue (scaledValue (lower, upper, 0.25f, useLog), 0.25f); if (LADSPA_IS_HINT_DEFAULT_MIDDLE (desc)) return ParameterValue (scaledValue (lower, upper, 0.50f, useLog), 0.50f); if (LADSPA_IS_HINT_DEFAULT_HIGH (desc)) return ParameterValue (scaledValue (lower, upper, 0.75f, useLog), 0.75f); } } return ParameterValue(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginInstance) }; //============================================================================== //============================================================================== LADSPAPluginFormat::LADSPAPluginFormat() {} LADSPAPluginFormat::~LADSPAPluginFormat() {} void LADSPAPluginFormat::findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier) { if (! fileMightContainThisPluginType (fileOrIdentifier)) return; PluginDescription desc; desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; ScopedPointer instance (dynamic_cast (createInstanceFromDescription (desc, 44100.0, 512))); if (instance == nullptr || ! instance->isValid()) return; instance->initialise (44100.0, 512); instance->fillInPluginDescription (desc); if (instance->module->moduleMain != nullptr) { for (int uid = 0;; ++uid) { if (const LADSPA_Descriptor* plugin = instance->module->moduleMain (uid)) { desc.uid = uid; desc.name = plugin->Name != nullptr ? plugin->Name : "Unknown"; if (! arrayContainsPlugin (results, desc)) results.add (new PluginDescription (desc)); } else { break; } } } } AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const PluginDescription& desc, double sampleRate, int blockSize) { ScopedPointer result; if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { File file (desc.fileOrIdentifier); const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); file.getParentDirectory().setAsCurrentWorkingDirectory(); const LADSPAModuleHandle::Ptr module (LADSPAModuleHandle::findOrCreateModule (file)); if (module != nullptr) { shellLADSPAUIDToCreate = desc.uid; result = new LADSPAPluginInstance (module); if (result->plugin != nullptr && result->isValid()) result->initialise (sampleRate, blockSize); else result = nullptr; } previousWorkingDirectory.setAsCurrentWorkingDirectory(); } return result.release(); } bool LADSPAPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); return f.existsAsFile() && f.hasFileExtension (".so"); } String LADSPAPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } bool LADSPAPluginFormat::pluginNeedsRescanning (const PluginDescription& desc) { return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime; } bool LADSPAPluginFormat::doesPluginStillExist (const PluginDescription& desc) { return File::createFileWithoutCheckingPath (desc.fileOrIdentifier).exists(); } StringArray LADSPAPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive) { StringArray results; for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) recursiveFileSearch (results, directoriesToSearch[j], recursive); return results; } void LADSPAPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive) { DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); while (iter.next()) { const File f (iter.getFile()); bool isPlugin = false; if (fileMightContainThisPluginType (f.getFullPathName())) { isPlugin = true; results.add (f.getFullPathName()); } if (recursive && (! isPlugin) && f.isDirectory()) recursiveFileSearch (results, f, true); } } FileSearchPath LADSPAPluginFormat::getDefaultLocationsToSearch() { return FileSearchPath (SystemStats::getEnvironmentVariable ("LADSPA_PATH", "/usr/lib/ladspa;/usr/local/lib/ladspa;~/.ladspa") .replace (":", ";")); } #endif juce_LADSPAPluginFormat.h000066400000000000000000000045751320201440200353200ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if (JUCE_PLUGINHOST_LADSPA && JUCE_LINUX) || DOXYGEN //============================================================================== /** Implements a plugin format manager for LADSPA plugins. */ class JUCE_API LADSPAPluginFormat : public AudioPluginFormat { public: //============================================================================== LADSPAPluginFormat(); ~LADSPAPluginFormat(); //============================================================================== String getName() const override { return "LADSPA"; } void findAllTypesForFile (OwnedArray&, const String& fileOrIdentifier) override; AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override; bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; bool pluginNeedsRescanning (const PluginDescription&) override; StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override; bool doesPluginStillExist (const PluginDescription&) override; FileSearchPath getDefaultLocationsToSearch() override; bool canScanForPlugins() const override { return true; } private: void recursiveFileSearch (StringArray&, const File&, bool recursive); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginFormat) }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h000066400000000000000000000422721320201440200340070ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_VST3COMMON_H_INCLUDED #define JUCE_VST3COMMON_H_INCLUDED //============================================================================== #define JUCE_DECLARE_VST3_COM_REF_METHODS \ Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \ Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } #define JUCE_DECLARE_VST3_COM_QUERY_METHODS \ Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \ { \ jassertfalse; \ *obj = nullptr; \ return Steinberg::kNotImplemented; \ } static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexcept { return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0; } #define TEST_FOR_AND_RETURN_IF_VALID(iidToTest, ClassType) \ if (doUIDsMatch (iidToTest, ClassType::iid)) \ { \ addRef(); \ *obj = dynamic_cast (this); \ return Steinberg::kResultOk; \ } #define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(iidToTest, CommonClassType, SourceClassType) \ if (doUIDsMatch (iidToTest, CommonClassType::iid)) \ { \ addRef(); \ *obj = (CommonClassType*) static_cast (this); \ return Steinberg::kResultOk; \ } //============================================================================== static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); } // NB: The casts are handled by a Steinberg::UString operator static juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast (string)); } static juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast (string)); } static void toString128 (Steinberg::Vst::String128 result, const juce::String& source) { Steinberg::UString (result, 128).fromAscii (source.toUTF8()); } static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept { return reinterpret_cast (source.toUTF16().getAddress()); } #if JUCE_WINDOWS static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; #else static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; #endif //============================================================================== static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor, bool isInput, int busIndex) { Steinberg::Vst::SpeakerArrangement arrangement = Steinberg::Vst::SpeakerArr::kEmpty; if (processor != nullptr) processor->getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput, (Steinberg::int32) busIndex, arrangement); return arrangement; } /** For the sake of simplicity, there can only be 1 arrangement type per channel count. i.e.: 4 channels == k31Cine OR k40Cine */ static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept { using namespace Steinberg::Vst::SpeakerArr; switch (numChannels) { case 0: return kEmpty; case 1: return kMono; case 2: return kStereo; case 3: return k30Cine; case 4: return k31Cine; case 5: return k50; case 6: return k51; case 7: return k61Cine; case 8: return k71CineFullFront; case 9: return k90; case 10: return k91; case 11: return k101; case 12: return k111; case 13: return k130; case 14: return k131; case 24: return (Steinberg::Vst::SpeakerArrangement) 1929904127; // k222 default: break; } jassert (numChannels >= 0); juce::BigInteger bi; bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true); return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); } //============================================================================== template class ComSmartPtr { public: ComSmartPtr() noexcept : source (nullptr) {} ComSmartPtr (ObjectType* object, bool autoAddRef = true) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); } ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); } ~ComSmartPtr() { if (source != nullptr) source->release(); } operator ObjectType*() const noexcept { return source; } ObjectType* get() const noexcept { return source; } ObjectType& operator*() const noexcept { return *source; } ObjectType* operator->() const noexcept { return source; } ComSmartPtr& operator= (const ComSmartPtr& other) { return operator= (other.source); } ComSmartPtr& operator= (ObjectType* const newObjectToTakePossessionOf) { ComSmartPtr p (newObjectToTakePossessionOf); std::swap (p.source, source); return *this; } bool operator== (ObjectType* const other) noexcept { return source == other; } bool operator!= (ObjectType* const other) noexcept { return source != other; } bool loadFrom (Steinberg::FUnknown* o) { *this = nullptr; return o != nullptr && o->queryInterface (ObjectType::iid, (void**) &source) == Steinberg::kResultOk; } bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid) { jassert (factory != nullptr); *this = nullptr; return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk; } private: ObjectType* source; }; //============================================================================== class MidiEventList : public Steinberg::Vst::IEventList { public: MidiEventList() {} virtual ~MidiEventList() {} JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS //============================================================================== void clear() { events.clearQuick(); } Steinberg::int32 PLUGIN_API getEventCount() override { return (Steinberg::int32) events.size(); } // NB: This has to cope with out-of-range indexes from some plugins. Steinberg::tresult PLUGIN_API getEvent (Steinberg::int32 index, Steinberg::Vst::Event& e) override { if (isPositiveAndBelow ((int) index, events.size())) { e = events.getReference ((int) index); return Steinberg::kResultTrue; } return Steinberg::kResultFalse; } Steinberg::tresult PLUGIN_API addEvent (Steinberg::Vst::Event& e) override { events.add (e); return Steinberg::kResultTrue; } //============================================================================== static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList) { const int32 numEvents = eventList.getEventCount(); for (Steinberg::int32 i = 0; i < numEvents; ++i) { Steinberg::Vst::Event e; if (eventList.getEvent (i, e) == Steinberg::kResultOk) { switch (e.type) { case Steinberg::Vst::Event::kNoteOnEvent: result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel), createSafeNote (e.noteOn.pitch), (Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity)), e.sampleOffset); break; case Steinberg::Vst::Event::kNoteOffEvent: result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel), createSafeNote (e.noteOff.pitch), (Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity)), e.sampleOffset); break; case Steinberg::Vst::Event::kPolyPressureEvent: result.addEvent (MidiMessage::aftertouchChange (createSafeChannel (e.polyPressure.channel), createSafeNote (e.polyPressure.pitch), denormaliseToMidiValue (e.polyPressure.pressure)), e.sampleOffset); break; case Steinberg::Vst::Event::kDataEvent: result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, (int) e.data.size), e.sampleOffset); break; default: break; } } } } static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer) { MidiBuffer::Iterator iterator (midiBuffer); MidiMessage msg; int midiEventPosition = 0; enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once int numEvents = 0; while (iterator.getNextEvent (msg, midiEventPosition)) { if (++numEvents > maxNumEvents) break; Steinberg::Vst::Event e = { 0 }; if (msg.isNoteOn()) { e.type = Steinberg::Vst::Event::kNoteOnEvent; e.noteOn.channel = createSafeChannel (msg.getChannel()); e.noteOn.pitch = createSafeNote (msg.getNoteNumber()); e.noteOn.velocity = normaliseMidiValue (msg.getVelocity()); e.noteOn.length = 0; e.noteOn.tuning = 0.0f; e.noteOn.noteId = -1; } else if (msg.isNoteOff()) { e.type = Steinberg::Vst::Event::kNoteOffEvent; e.noteOff.channel = createSafeChannel (msg.getChannel()); e.noteOff.pitch = createSafeNote (msg.getNoteNumber()); e.noteOff.velocity = normaliseMidiValue (msg.getVelocity()); e.noteOff.tuning = 0.0f; e.noteOff.noteId = -1; } else if (msg.isSysEx()) { e.type = Steinberg::Vst::Event::kDataEvent; e.data.bytes = msg.getSysExData(); e.data.size = (uint32) msg.getSysExDataSize(); e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; } else if (msg.isAftertouch()) { e.type = Steinberg::Vst::Event::kPolyPressureEvent; e.polyPressure.channel = createSafeChannel (msg.getChannel()); e.polyPressure.pitch = createSafeNote (msg.getNoteNumber()); e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue()); } else { continue; } e.busIndex = 0; e.sampleOffset = midiEventPosition; result.addEvent (e); } } private: Array events; Atomic refCount; static Steinberg::int16 createSafeChannel (int channel) noexcept { return (Steinberg::int16) jlimit (0, 15, channel - 1); } static int createSafeChannel (Steinberg::int16 channel) noexcept { return (int) jlimit (1, 16, channel + 1); } static Steinberg::int16 createSafeNote (int note) noexcept { return (Steinberg::int16) jlimit (0, 127, note); } static int createSafeNote (Steinberg::int16 note) noexcept { return jlimit (0, 127, (int) note); } static float normaliseMidiValue (int value) noexcept { return jlimit (0.0f, 1.0f, (float) value / 127.0f); } static int denormaliseToMidiValue (float value) noexcept { return roundToInt (jlimit (0.0f, 127.0f, value * 127.0f)); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList) }; //============================================================================== namespace VST3BufferExchange { typedef Array Bus; typedef Array BusMap; /** Assigns a series of AudioSampleBuffer's channels to an AudioBusBuffers' @warning For speed, does not check the channel count and offsets according to the AudioSampleBuffer */ void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers, Bus& bus, AudioSampleBuffer& buffer, int numChannels, int channelStartOffset, int sampleOffset = 0) { const int channelEnd = numChannels + channelStartOffset; jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels()); bus.clearQuick(); for (int i = channelStartOffset; i < channelEnd; ++i) bus.add (buffer.getWritePointer (i, sampleOffset)); vstBuffers.channelBuffers32 = bus.getRawDataPointer(); vstBuffers.numChannels = numChannels; vstBuffers.silenceFlags = 0; } static void mapArrangementToBusses (int& channelIndexOffset, int index, Array& result, BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement, AudioSampleBuffer& source) { const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits(); if (index >= result.size()) result.add (Steinberg::Vst::AudioBusBuffers()); if (index >= busMapToUse.size()) busMapToUse.add (Bus()); if (numChansForBus > 0) { associateBufferTo (result.getReference (index), busMapToUse.getReference (index), source, numChansForBus, channelIndexOffset); } channelIndexOffset += numChansForBus; } static void mapBufferToBusses (Array& result, BusMap& busMapToUse, const Array& arrangements, AudioSampleBuffer& source) { int channelIndexOffset = 0; for (int i = 0; i < arrangements.size(); ++i) mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse, arrangements.getUnchecked (i), source); } static void mapBufferToBusses (Array& result, Steinberg::Vst::IAudioProcessor& processor, BusMap& busMapToUse, bool isInput, int numBusses, AudioSampleBuffer& source) { int channelIndexOffset = 0; for (int i = 0; i < numBusses; ++i) mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse, getArrangementForBus (&processor, isInput, i), source); } } #endif // JUCE_VST3COMMON_H_INCLUDED juce_VST3Headers.h000066400000000000000000000132411320201440200340450ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_VST3HEADERS_H_INCLUDED #define JUCE_VST3HEADERS_H_INCLUDED #undef Point #undef Component // Wow, those Steinberg guys really don't worry too much about compiler warnings. #if _MSC_VER #pragma warning (disable: 4505) #pragma warning (push, 0) #pragma warning (disable: 4702) #elif __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnon-virtual-dtor" #pragma clang diagnostic ignored "-Wreorder" #pragma clang diagnostic ignored "-Wunsequenced" #pragma clang diagnostic ignored "-Wint-to-pointer-cast" #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Woverloaded-virtual" #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wdeprecated-register" #endif /* These files come with the Steinberg VST3 SDK - to get them, you'll need to visit the Steinberg website and agree to whatever is currently required to get them. Then, you'll need to make sure your include path contains your "VST3 SDK" directory (or whatever you've named it on your machine). The Introjucer has a special box for setting this path. */ #if JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================== namespace Steinberg { /** Missing IIDs */ DEF_CLASS_IID (IPluginBase) DEF_CLASS_IID (IPlugView) DEF_CLASS_IID (IPlugFrame) DEF_CLASS_IID (IBStream) DEF_CLASS_IID (ISizeableStream) DEF_CLASS_IID (IPluginFactory) DEF_CLASS_IID (IPluginFactory2) DEF_CLASS_IID (IPluginFactory3) } #endif //JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY #if _MSC_VER #pragma warning (pop) #elif __clang__ #pragma clang diagnostic pop #endif //============================================================================== #undef ASSERT #undef WARNING #undef PRINTSYSERROR #undef DEBUGSTR #undef DBPRT0 #undef DBPRT1 #undef DBPRT2 #undef DBPRT3 #undef DBPRT4 #undef DBPRT5 #undef min #undef max #undef MIN #undef MAX #undef calloc #undef free #undef malloc #undef realloc #undef NEW #undef NEWVEC #undef VERIFY #undef VERIFY_IS #undef VERIFY_NOT #undef META_CREATE_FUNC #undef CLASS_CREATE_FUNC #undef SINGLE_CREATE_FUNC #undef _META_CLASS #undef _META_CLASS_IFACE #undef _META_CLASS_SINGLE #undef META_CLASS #undef META_CLASS_IFACE #undef META_CLASS_SINGLE #undef SINGLETON #undef OBJ_METHODS #undef QUERY_INTERFACE #undef LICENCE_UID #undef BEGIN_FACTORY #undef DEF_CLASS #undef DEF_CLASS1 #undef DEF_CLASS2 #undef DEF_CLASS_W #undef END_FACTORY #undef Point #undef Component #endif // JUCE_VST3HEADERS_H_INCLUDED juce_VST3PluginFormat.cpp000066400000000000000000002562161320201440200354470ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_PLUGINHOST_VST3 } // namespace juce #if JucePlugin_Build_VST3 #undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY #define JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY 1 #endif #include #include "juce_VST3Headers.h" #undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY namespace juce { #include "juce_VST3Common.h" using namespace Steinberg; //============================================================================== struct VST3Classes { #ifndef JUCE_VST3_DEBUGGING #define JUCE_VST3_DEBUGGING 0 #endif #if JUCE_VST3_DEBUGGING #define VST3_DBG(a) Logger::writeToLog (a); #else #define VST3_DBG(a) #endif #if JUCE_DEBUG static int warnOnFailure (int result) { const char* message = "Unknown result!"; switch (result) { case kResultOk: return result; case kNotImplemented: message = "kNotImplemented"; break; case kNoInterface: message = "kNoInterface"; break; case kResultFalse: message = "kResultFalse"; break; case kInvalidArgument: message = "kInvalidArgument"; break; case kInternalError: message = "kInternalError"; break; case kNotInitialized: message = "kNotInitialized"; break; case kOutOfMemory: message = "kOutOfMemory"; break; default: break; } DBG (message); return result; } #else #define warnOnFailure(x) x #endif //============================================================================== static int getHashForTUID (const TUID& tuid) noexcept { int value = 0; for (int i = 0; i < numElementsInArray (tuid); ++i) value = (value * 31) + tuid[i]; return value; } template static void fillDescriptionWith (PluginDescription& description, ObjectType& object) { description.version = toString (object.version).trim(); description.category = toString (object.subCategories).trim(); if (description.manufacturerName.trim().isEmpty()) description.manufacturerName = toString (object.vendor).trim(); } static void createPluginDescription (PluginDescription& description, const File& pluginFile, const String& company, const String& name, const PClassInfo& info, PClassInfo2* info2, PClassInfoW* infoW, int numInputs, int numOutputs) { description.fileOrIdentifier = pluginFile.getFullPathName(); description.lastFileModTime = pluginFile.getLastModificationTime(); description.manufacturerName = company; description.name = name; description.descriptiveName = name; description.pluginFormatName = "VST3"; description.numInputChannels = numInputs; description.numOutputChannels = numOutputs; description.uid = getHashForTUID (info.cid); if (infoW != nullptr) fillDescriptionWith (description, *infoW); else if (info2 != nullptr) fillDescriptionWith (description, *info2); if (description.category.isEmpty()) description.category = toString (info.category).trim(); description.isInstrument = description.category.containsIgnoreCase ("Instrument"); // This seems to be the only way to find that out! ARGH! } static int getNumSingleDirectionBussesFor (Vst::IComponent* component, bool checkInputs, bool checkAudioChannels) { jassert (component != nullptr); return (int) component->getBusCount (checkAudioChannels ? Vst::kAudio : Vst::kEvent, checkInputs ? Vst::kInput : Vst::kOutput); } /** Gives the total number of channels for a particular type of bus direction and media type */ static int getNumSingleDirectionChannelsFor (Vst::IComponent* component, bool checkInputs, bool checkAudioChannels) { jassert (component != nullptr); const Vst::BusDirections direction = checkInputs ? Vst::kInput : Vst::kOutput; const Vst::MediaTypes mediaType = checkAudioChannels ? Vst::kAudio : Vst::kEvent; const Steinberg::int32 numBuses = component->getBusCount (mediaType, direction); int numChannels = 0; for (Steinberg::int32 i = numBuses; --i >= 0;) { Vst::BusInfo busInfo; warnOnFailure (component->getBusInfo (mediaType, direction, i, busInfo)); numChannels += (int) busInfo.channelCount; } return numChannels; } static void setStateForAllBussesOfType (Vst::IComponent* component, bool state, bool activateInputs, bool activateAudioChannels) { jassert (component != nullptr); const Vst::BusDirections direction = activateInputs ? Vst::kInput : Vst::kOutput; const Vst::MediaTypes mediaType = activateAudioChannels ? Vst::kAudio : Vst::kEvent; const Steinberg::int32 numBuses = component->getBusCount (mediaType, direction); for (Steinberg::int32 i = numBuses; --i >= 0;) warnOnFailure (component->activateBus (mediaType, direction, i, state)); } //============================================================================== /** Assigns a complete AudioSampleBuffer's channels to an AudioBusBuffers' */ static void associateWholeBufferTo (Vst::AudioBusBuffers& vstBuffers, AudioSampleBuffer& buffer) noexcept { vstBuffers.channelBuffers32 = buffer.getArrayOfWritePointers(); vstBuffers.numChannels = buffer.getNumChannels(); vstBuffers.silenceFlags = 0; } //============================================================================== static void toProcessContext (Vst::ProcessContext& context, AudioPlayHead* playHead, double sampleRate) { jassert (sampleRate > 0.0); //Must always be valid, as stated by the VST3 SDK using namespace Vst; zerostruct (context); context.sampleRate = sampleRate; if (playHead != nullptr) { AudioPlayHead::CurrentPositionInfo position; playHead->getCurrentPosition (position); context.projectTimeSamples = position.timeInSamples; //Must always be valid, as stated by the VST3 SDK context.projectTimeMusic = position.timeInSeconds; //Does not always need to be valid... context.tempo = position.bpm; context.timeSigNumerator = position.timeSigNumerator; context.timeSigDenominator = position.timeSigDenominator; context.barPositionMusic = position.ppqPositionOfLastBarStart; context.cycleStartMusic = position.ppqLoopStart; context.cycleEndMusic = position.ppqLoopEnd; switch (position.frameRate) { case AudioPlayHead::fps24: context.frameRate.framesPerSecond = 24; break; case AudioPlayHead::fps25: context.frameRate.framesPerSecond = 25; break; case AudioPlayHead::fps30: context.frameRate.framesPerSecond = 30; break; case AudioPlayHead::fps2997: case AudioPlayHead::fps2997drop: case AudioPlayHead::fps30drop: { context.frameRate.framesPerSecond = 30; context.frameRate.flags = FrameRate::kDropRate; if (position.frameRate == AudioPlayHead::fps2997drop) context.frameRate.flags |= FrameRate::kPullDownRate; } break; case AudioPlayHead::fpsUnknown: break; default: jassertfalse; break; // New frame rate? } if (position.isPlaying) context.state |= ProcessContext::kPlaying; if (position.isRecording) context.state |= ProcessContext::kRecording; if (position.isLooping) context.state |= ProcessContext::kCycleActive; } else { context.tempo = 120.0; context.frameRate.framesPerSecond = 30; context.timeSigNumerator = 4; context.timeSigDenominator = 4; } if (context.projectTimeMusic >= 0.0) context.state |= ProcessContext::kProjectTimeMusicValid; if (context.barPositionMusic >= 0.0) context.state |= ProcessContext::kBarPositionValid; if (context.tempo > 0.0) context.state |= ProcessContext::kTempoValid; if (context.frameRate.framesPerSecond > 0) context.state |= ProcessContext::kSmpteValid; if (context.cycleStartMusic >= 0.0 && context.cycleEndMusic > 0.0 && context.cycleEndMusic > context.cycleStartMusic) { context.state |= ProcessContext::kCycleValid; } if (context.timeSigNumerator > 0 && context.timeSigDenominator > 0) context.state |= ProcessContext::kTimeSigValid; } //============================================================================== /** Get a list of speaker arrangements as per their speaker names (e.g.: 2 regular channels, aliased as 'kStringStereoS', is "L R") */ static StringArray getSpeakerArrangements() { using namespace Vst::SpeakerArr; const Vst::CString arrangements[] = { kStringMonoS, kStringStereoS, kStringStereoRS, kStringStereoCS, kStringStereoSS, kStringStereoCLfeS, kString30CineS, kString30MusicS, kString31CineS, kString31MusicS, kString40CineS, kString40MusicS, kString41CineS, kString41MusicS, kString50S, kString51S, kString60CineS, kString60MusicS, kString61CineS, kString61MusicS, kString70CineS, kString70MusicS, kString71CineS, kString71MusicS, kString80CineS, kString80MusicS, kString81CineS, kString81MusicS, kString80CubeS, kStringBFormat1stOrderS, kString71CineTopCenterS, kString71CineCenterHighS, kString71CineFrontHighS, kString71CineSideHighS, kString71CineFullRearS, kString90S, kString91S, kString100S, kString101S, kString110S, kString111S, kString130S, kString131S, kString102S, kString122S, nullptr }; return StringArray (arrangements); } /** Get a list of speaker arrangements as per their named configurations (e.g.: 2 regular channels, aliased as 'kStringStereoS', is "L R") */ static StringArray getNamedSpeakerArrangements() { using namespace Vst::SpeakerArr; const Vst::CString arrangements[] = { kStringEmpty, kStringMono, kStringStereo, kStringStereoR, kStringStereoC, kStringStereoSide, kStringStereoCLfe, kString30Cine, kString30Music, kString31Cine, kString31Music, kString40Cine, kString40Music, kString41Cine, kString41Music, kString50, kString51, kString60Cine, kString60Music, kString61Cine, kString61Music, kString70Cine, kString70Music, kString71Cine, kString71Music, kString71CineTopCenter, kString71CineCenterHigh, kString71CineFrontHigh, kString71CineSideHigh, kString71CineFullRear, kString80Cine, kString80Music, kString80Cube, kString81Cine, kString81Music, kString102, kString122, kString90, kString91, kString100, kString101, kString110, kString111, kString130, kString131, nullptr }; return StringArray (arrangements); } static Vst::SpeakerArrangement getSpeakerArrangementFrom (const String& string) { return Vst::SpeakerArr::getSpeakerArrangementFromString (string.toUTF8()); } //============================================================================== static StringArray getPluginEffectCategories() { using namespace Vst::PlugType; const Vst::CString categories[] = { kFxAnalyzer, kFxDelay, kFxDistortion, kFxDynamics, kFxEQ, kFxFilter, kFx, kFxInstrument, kFxInstrumentExternal, kFxSpatial, kFxGenerator, kFxMastering, kFxModulation, kFxPitchShift, kFxRestoration, kFxReverb, kFxSurround, kFxTools, kSpatial, kSpatialFx, nullptr }; return StringArray (categories); } static StringArray getPluginInstrumentCategories() { using namespace Vst::PlugType; const Vst::CString categories[] = { kInstrumentSynthSampler, kInstrumentDrum, kInstrumentSampler, kInstrumentSynth, kInstrumentExternal, kFxInstrument, kFxInstrumentExternal, kFxSpatial, kFxGenerator, nullptr }; return StringArray (categories); } //============================================================================== class VST3PluginInstance; class VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0 public Vst::IComponentHandler2, // From VST V3.1.0 (a very well named class, of course!) public Vst::IComponentHandler3, // From VST V3.5.0 (also very well named!) public Vst::IContextMenuTarget, public Vst::IHostApplication, public Vst::IUnitHandler { public: VST3HostContext (VST3PluginInstance* pluginInstance) : owner (pluginInstance) { appName = File::getSpecialLocation (File::currentApplicationFile).getFileNameWithoutExtension(); attributeList = new AttributeList (this); } virtual ~VST3HostContext() {} JUCE_DECLARE_VST3_COM_REF_METHODS FUnknown* getFUnknown() { return static_cast (this); } static bool hasFlag (Steinberg::int32 source, Steinberg::int32 flag) noexcept { return (source & flag) == flag; } //============================================================================== tresult PLUGIN_API beginEdit (Vst::ParamID paramID) override { const int index = getIndexOfParamID (paramID); if (index < 0) return kResultFalse; owner->beginParameterChangeGesture (index); return kResultTrue; } tresult PLUGIN_API performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalized) override { const int index = getIndexOfParamID (paramID); if (index < 0) return kResultFalse; owner->sendParamChangeMessageToListeners (index, (float) valueNormalized); return owner->editController->setParamNormalized (paramID, valueNormalized); } tresult PLUGIN_API endEdit (Vst::ParamID paramID) override { const int index = getIndexOfParamID (paramID); if (index < 0) return kResultFalse; owner->endParameterChangeGesture (index); return kResultTrue; } tresult PLUGIN_API restartComponent (Steinberg::int32 flags) override { if (owner != nullptr) { if (hasFlag (flags, Vst::kReloadComponent)) owner->reset(); if (hasFlag (flags, Vst::kIoChanged)) { const double sampleRate = owner->getSampleRate(); const int blockSize = owner->getBlockSize(); owner->prepareToPlay (sampleRate >= 8000 ? sampleRate : 44100.0, blockSize > 0 ? blockSize : 1024); } if (hasFlag (flags, Vst::kLatencyChanged)) if (owner->processor != nullptr) owner->setLatencySamples (jmax (0, (int) owner->processor->getLatencySamples())); owner->updateHostDisplay(); return kResultTrue; } jassertfalse; return kResultFalse; } //============================================================================== tresult PLUGIN_API setDirty (TBool) override { return kResultFalse; } tresult PLUGIN_API requestOpenEditor (FIDString name) override { (void) name; jassertfalse; return kResultFalse; } tresult PLUGIN_API startGroupEdit() override { jassertfalse; return kResultFalse; } tresult PLUGIN_API finishGroupEdit() override { jassertfalse; return kResultFalse; } //============================================================================== class ContextMenu : public Vst::IContextMenu { public: ContextMenu (VST3PluginInstance& pluginInstance) : owner (pluginInstance) {} virtual ~ContextMenu() {} JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS Steinberg::int32 PLUGIN_API getItemCount() override { return (Steinberg::int32) items.size(); } tresult PLUGIN_API addItem (const Item& item, IContextMenuTarget* target) override { jassert (target != nullptr); ItemAndTarget newItem; newItem.item = item; newItem.target = target; items.add (newItem); return kResultOk; } tresult PLUGIN_API removeItem (const Item& toRemove, IContextMenuTarget* target) override { for (int i = items.size(); --i >= 0;) { ItemAndTarget& item = items.getReference(i); if (item.item.tag == toRemove.tag && item.target == target) items.remove (i); } return kResultOk; } tresult PLUGIN_API getItem (Steinberg::int32 tag, Item& result, IContextMenuTarget** target) override { for (int i = 0; i < items.size(); ++i) { const ItemAndTarget& item = items.getReference(i); if (item.item.tag == tag) { result = item.item; if (target != nullptr) *target = item.target; return kResultTrue; } } zerostruct (result); return kResultFalse; } tresult PLUGIN_API popup (Steinberg::UCoord x, Steinberg::UCoord y) override { Array subItemStack; OwnedArray menuStack; PopupMenu* topLevelMenu = menuStack.add (new PopupMenu()); for (int i = 0; i < items.size(); ++i) { const Item& item = items.getReference (i).item; PopupMenu* menuToUse = menuStack.getLast(); if (hasFlag (item.flags, Item::kIsGroupStart & ~Item::kIsDisabled)) { subItemStack.add (&item); menuStack.add (new PopupMenu()); } else if (hasFlag (item.flags, Item::kIsGroupEnd)) { if (const Item* subItem = subItemStack.getLast()) { if (PopupMenu* m = menuStack [menuStack.size() - 2]) m->addSubMenu (toString (subItem->name), *menuToUse, ! hasFlag (subItem->flags, Item::kIsDisabled), nullptr, hasFlag (subItem->flags, Item::kIsChecked)); menuStack.removeLast (1); subItemStack.removeLast (1); } } else if (hasFlag (item.flags, Item::kIsSeparator)) { menuToUse->addSeparator(); } else { menuToUse->addItem (item.tag != 0 ? (int) item.tag : (int) zeroTagReplacement, toString (item.name), ! hasFlag (item.flags, Item::kIsDisabled), hasFlag (item.flags, Item::kIsChecked)); } } PopupMenu::Options options; if (AudioProcessorEditor* ed = owner.getActiveEditor()) options = options.withTargetScreenArea (ed->getScreenBounds().translated ((int) x, (int) y).withSize (1, 1)); #if JUCE_MODAL_LOOPS_PERMITTED // Unfortunately, Steinberg's docs explicitly say this should be modal.. handleResult (topLevelMenu->showMenu (options)); #else topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, ComSmartPtr (this))); #endif return kResultOk; } #if ! JUCE_MODAL_LOOPS_PERMITTED static void menuFinished (int modalResult, ComSmartPtr menu) { menu->handleResult (modalResult); } #endif private: enum { zeroTagReplacement = 0x7fffffff }; Atomic refCount; VST3PluginInstance& owner; struct ItemAndTarget { Item item; ComSmartPtr target; }; Array items; void handleResult (int result) { if (result == 0) return; if (result == zeroTagReplacement) result = 0; for (int i = 0; i < items.size(); ++i) { const ItemAndTarget& item = items.getReference(i); if ((int) item.item.tag == result) { if (item.target != nullptr) item.target->executeMenuItem ((Steinberg::int32) result); break; } } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContextMenu) }; Vst::IContextMenu* PLUGIN_API createContextMenu (IPlugView*, const Vst::ParamID*) override { if (owner != nullptr) return new ContextMenu (*owner); return nullptr; } tresult PLUGIN_API executeMenuItem (Steinberg::int32) override { jassertfalse; return kResultFalse; } //============================================================================== tresult PLUGIN_API getName (Vst::String128 name) override { Steinberg::String str (appName.toUTF8()); str.copyTo (name, 0, 127); return kResultOk; } tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) override { *obj = nullptr; if (! doUIDsMatch (cid, iid)) { jassertfalse; return kInvalidArgument; } if (doUIDsMatch (cid, Vst::IMessage::iid) && doUIDsMatch (iid, Vst::IMessage::iid)) { ComSmartPtr m (new Message (*this, attributeList)); messageQueue.add (m); m->addRef(); *obj = m; return kResultOk; } else if (doUIDsMatch (cid, Vst::IAttributeList::iid) && doUIDsMatch (iid, Vst::IAttributeList::iid)) { ComSmartPtr l (new AttributeList (this)); l->addRef(); *obj = l; return kResultOk; } jassertfalse; return kNotImplemented; } //============================================================================== tresult PLUGIN_API notifyUnitSelection (Vst::UnitID) override { jassertfalse; return kResultFalse; } tresult PLUGIN_API notifyProgramListChange (Vst::ProgramListID, Steinberg::int32) override { jassertfalse; return kResultFalse; } //============================================================================== tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override { if (doUIDsMatch (iid, Vst::IAttributeList::iid)) { *obj = attributeList.get(); return kResultOk; } TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler2) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler3) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IContextMenuTarget) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IHostApplication) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IUnitHandler) TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (iid, FUnknown, Vst::IComponentHandler) *obj = nullptr; return kNotImplemented; } private: //============================================================================== VST3PluginInstance* const owner; Atomic refCount; String appName; typedef std::map ParamMapType; ParamMapType paramToIndexMap; int getIndexOfParamID (Vst::ParamID paramID) { if (owner == nullptr || owner->editController == nullptr) return -1; int result = getMappedParamID (paramID); if (result < 0) { const int numParams = owner->editController->getParameterCount(); for (int i = 0; i < numParams; ++i) { Vst::ParameterInfo paramInfo; owner->editController->getParameterInfo (i, paramInfo); paramToIndexMap[paramInfo.id] = i; } result = getMappedParamID (paramID); } return result; } int getMappedParamID (Vst::ParamID paramID) { const ParamMapType::iterator it (paramToIndexMap.find (paramID)); return it != paramToIndexMap.end() ? it->second : -1; } //============================================================================== class Message : public Vst::IMessage { public: Message (VST3HostContext& o, Vst::IAttributeList* list) : owner (o), attributeList (list) { } Message (VST3HostContext& o, Vst::IAttributeList* list, FIDString id) : owner (o), attributeList (list), messageId (toString (id)) { } Message (VST3HostContext& o, Vst::IAttributeList* list, FIDString id, const var& v) : value (v), owner (o), attributeList (list), messageId (toString (id)) { } virtual ~Message() {} JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS FIDString PLUGIN_API getMessageID() override { return messageId.toRawUTF8(); } void PLUGIN_API setMessageID (FIDString id) override { messageId = toString (id); } Vst::IAttributeList* PLUGIN_API getAttributes() override { return attributeList; } var value; private: VST3HostContext& owner; ComSmartPtr attributeList; String messageId; Atomic refCount; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message) }; Array, CriticalSection> messageQueue; //============================================================================== class AttributeList : public Vst::IAttributeList { public: AttributeList (VST3HostContext* o) : owner (o) {} virtual ~AttributeList() {} JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS //============================================================================== tresult PLUGIN_API setInt (AttrID id, Steinberg::int64 value) override { addMessageToQueue (id, value); return kResultTrue; } tresult PLUGIN_API setFloat (AttrID id, double value) override { addMessageToQueue (id, value); return kResultTrue; } tresult PLUGIN_API setString (AttrID id, const Vst::TChar* string) override { addMessageToQueue (id, toString (string)); return kResultTrue; } tresult PLUGIN_API setBinary (AttrID id, const void* data, Steinberg::uint32 size) override { jassert (size >= 0 && (data != nullptr || size == 0)); addMessageToQueue (id, MemoryBlock (data, (size_t) size)); return kResultTrue; } //============================================================================== tresult PLUGIN_API getInt (AttrID id, Steinberg::int64& result) override { jassert (id != nullptr); if (findMessageOnQueueWithID (id, result)) return kResultTrue; jassertfalse; return kResultFalse; } tresult PLUGIN_API getFloat (AttrID id, double& result) override { jassert (id != nullptr); if (findMessageOnQueueWithID (id, result)) return kResultTrue; jassertfalse; return kResultFalse; } tresult PLUGIN_API getString (AttrID id, Vst::TChar* result, Steinberg::uint32 length) override { jassert (id != nullptr); String stringToFetch; if (findMessageOnQueueWithID (id, stringToFetch)) { Steinberg::String str (stringToFetch.toRawUTF8()); str.copyTo (result, 0, (Steinberg::int32) jmin (length, (Steinberg::uint32) std::numeric_limits::max())); return kResultTrue; } jassertfalse; return kResultFalse; } tresult PLUGIN_API getBinary (AttrID id, const void*& data, Steinberg::uint32& size) override { jassert (id != nullptr); for (int i = owner->messageQueue.size(); --i >= 0;) { Message* const message = owner->messageQueue.getReference (i); if (std::strcmp (message->getMessageID(), id) == 0) { if (MemoryBlock* binaryData = message->value.getBinaryData()) { data = binaryData->getData(); size = (Steinberg::uint32) binaryData->getSize(); return kResultTrue; } } } return kResultFalse; } private: VST3HostContext* owner; Atomic refCount; //============================================================================== template void addMessageToQueue (AttrID id, const Type& value) { jassert (id != nullptr); for (int i = owner->messageQueue.size(); --i >= 0;) { VST3HostContext::Message* const message = owner->messageQueue.getReference (i); if (std::strcmp (message->getMessageID(), id) == 0) { message->value = value; return; } } owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, value))); } template bool findMessageOnQueueWithID (AttrID id, Type& value) { jassert (id != nullptr); for (int i = owner->messageQueue.size(); --i >= 0;) { VST3HostContext::Message* const message = owner->messageQueue.getReference (i); if (std::strcmp (message->getMessageID(), id) == 0) { value = message->value; return true; } } return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttributeList) }; ComSmartPtr attributeList; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3HostContext) }; //============================================================================== class DescriptionFactory { public: DescriptionFactory (VST3HostContext* host, IPluginFactory* pluginFactory) : vst3HostContext (host), factory (pluginFactory) { jassert (pluginFactory != nullptr); } virtual ~DescriptionFactory() {} Result findDescriptionsAndPerform (const File& file) { StringArray foundNames; PFactoryInfo factoryInfo; factory->getFactoryInfo (&factoryInfo); const String companyName (toString (factoryInfo.vendor).trim()); Result result (Result::ok()); const Steinberg::int32 numClasses = factory->countClasses(); for (Steinberg::int32 i = 0; i < numClasses; ++i) { PClassInfo info; factory->getClassInfo (i, &info); if (std::strcmp (info.category, kVstAudioEffectClass) != 0) continue; const String name (toString (info.name).trim()); if (foundNames.contains (name, true)) continue; ScopedPointer info2; ScopedPointer infoW; { ComSmartPtr pf2; ComSmartPtr pf3; if (pf2.loadFrom (factory)) { info2 = new PClassInfo2(); pf2->getClassInfo2 (i, info2); } if (pf3.loadFrom (factory)) { infoW = new PClassInfoW(); pf3->getClassInfoUnicode (i, infoW); } } foundNames.add (name); PluginDescription desc; { ComSmartPtr component; if (component.loadFrom (factory, info.cid)) { if (component->initialize (vst3HostContext->getFUnknown()) == kResultOk) { const int numInputs = getNumSingleDirectionChannelsFor (component, true, true); const int numOutputs = getNumSingleDirectionChannelsFor (component, false, true); createPluginDescription (desc, file, companyName, name, info, info2, infoW, numInputs, numOutputs); component->terminate(); } else { jassertfalse; } } else { jassertfalse; } } result = performOnDescription (desc); if (result.failed()) break; } return result; } protected: virtual Result performOnDescription (PluginDescription& description) = 0; private: ComSmartPtr vst3HostContext; ComSmartPtr factory; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionFactory) }; struct MatchingDescriptionFinder : public DescriptionFactory { MatchingDescriptionFinder (VST3HostContext* host, IPluginFactory* pluginFactory, const PluginDescription& desc) : DescriptionFactory (host, pluginFactory), description (desc) { } static const char* getSuccessString() noexcept { return "Found Description"; } Result performOnDescription (PluginDescription& desc) { if (description.isDuplicateOf (desc)) return Result::fail (getSuccessString()); return Result::ok(); } private: const PluginDescription& description; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MatchingDescriptionFinder) }; struct DescriptionLister : public DescriptionFactory { DescriptionLister (VST3HostContext* host, IPluginFactory* pluginFactory) : DescriptionFactory (host, pluginFactory) { } Result performOnDescription (PluginDescription& desc) { list.add (new PluginDescription (desc)); return Result::ok(); } OwnedArray list; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionLister) }; //============================================================================== struct DLLHandle { DLLHandle (const String& modulePath) : factory (nullptr) { if (modulePath.trim().isNotEmpty()) open (modulePath); } ~DLLHandle() { typedef bool (PLUGIN_API *ExitModuleFn) (); #if JUCE_WINDOWS if (ExitModuleFn exitFn = (ExitModuleFn) getFunction ("ExitDll")) exitFn(); releaseFactory(); library.close(); #else if (bundleRef != nullptr) { if (ExitModuleFn exitFn = (ExitModuleFn) getFunction ("bundleExit")) exitFn(); releaseFactory(); CFRelease (bundleRef); bundleRef = nullptr; } #endif } void open (const PluginDescription& description) { #if JUCE_WINDOWS jassert (description.fileOrIdentifier.isNotEmpty()); jassert (File (description.fileOrIdentifier).existsAsFile()); library.open (description.fileOrIdentifier); #else open (description.fileOrIdentifier); #endif } /** @note The factory should begin with a refCount of 1, so don't increment the reference count (ie: don't use a ComSmartPtr in here)! Its lifetime will be handled by this DllHandle, when such will be destroyed. @see releaseFactory */ IPluginFactory* JUCE_CALLTYPE getPluginFactory() { if (factory == nullptr) if (GetFactoryProc proc = (GetFactoryProc) getFunction ("GetPluginFactory")) factory = proc(); jassert (factory != nullptr); // The plugin NEEDS to provide a factory to be able to be called a VST3! return factory; } void* getFunction (const char* functionName) { #if JUCE_WINDOWS return library.getFunction (functionName); #else if (bundleRef == nullptr) return nullptr; CFStringRef name = String (functionName).toCFString(); void* fn = CFBundleGetFunctionPointerForName (bundleRef, name); CFRelease (name); return fn; #endif } private: IPluginFactory* factory; void releaseFactory() { if (factory != nullptr) factory->release(); } #if JUCE_WINDOWS DynamicLibrary library; bool open (const String& filePath) { if (library.open (filePath)) { typedef bool (PLUGIN_API *InitModuleProc) (); if (InitModuleProc proc = (InitModuleProc) getFunction ("InitDll")) { if (proc()) return true; } else { return true; } library.close(); } return false; } #else CFBundleRef bundleRef; bool open (const String& filePath) { const File file (filePath); const char* const utf8 = file.getFullPathName().toRawUTF8(); if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, (CFIndex) std::strlen (utf8), file.isDirectory())) { bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); if (bundleRef != nullptr) { CFErrorRef error = nullptr; if (CFBundleLoadExecutableAndReturnError (bundleRef, &error)) { typedef bool (*BundleEntryProc)(CFBundleRef); if (BundleEntryProc proc = (BundleEntryProc) getFunction ("bundleEntry")) { if (proc (bundleRef)) return true; } else { return true; } } if (error != nullptr) { if (CFStringRef failureMessage = CFErrorCopyFailureReason (error)) { DBG (String::fromCFString (failureMessage)); CFRelease (failureMessage); } CFRelease (error); } CFRelease (bundleRef); bundleRef = nullptr; } } return false; } #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DLLHandle) }; //============================================================================== class VST3ModuleHandle : public ReferenceCountedObject { public: explicit VST3ModuleHandle (const File& pluginFile) : file (pluginFile) { getActiveModules().add (this); } ~VST3ModuleHandle() { getActiveModules().removeFirstMatchingValue (this); } /** Since there is no apparent indication if a VST3 plugin is a shell or not, we're stuck iterating through a VST3's factory, creating a description for every housed plugin. */ static bool getAllDescriptionsForFile (OwnedArray& results, const String& fileOrIdentifier) { DLLHandle tempModule (fileOrIdentifier); ComSmartPtr pluginFactory (tempModule.getPluginFactory()); if (pluginFactory != nullptr) { ComSmartPtr host (new VST3HostContext (nullptr)); DescriptionLister lister (host, pluginFactory); const Result result (lister.findDescriptionsAndPerform (File (fileOrIdentifier))); results.addCopiesOf (lister.list); return result.wasOk(); } jassertfalse; return false; } //============================================================================== typedef ReferenceCountedObjectPtr Ptr; static VST3ModuleHandle::Ptr findOrCreateModule (const File& file, const PluginDescription& description) { Array& activeModules = getActiveModules(); for (int i = activeModules.size(); --i >= 0;) { VST3ModuleHandle* const module = activeModules.getUnchecked (i); // VST3s are basically shells, you must therefore check their name along with their file: if (module->file == file && module->name == description.name) return module; } VST3ModuleHandle::Ptr m (new VST3ModuleHandle (file)); if (! m->open (file, description)) m = nullptr; return m; } //============================================================================== IPluginFactory* getPluginFactory() { return dllHandle->getPluginFactory(); } File file; String name; private: ScopedPointer dllHandle; //============================================================================== static Array& getActiveModules() { static Array activeModules; return activeModules; } //============================================================================== bool open (const File& f, const PluginDescription& description) { dllHandle = new DLLHandle (f.getFullPathName()); ComSmartPtr pluginFactory (dllHandle->getPluginFactory()); if (pluginFactory != nullptr) { ComSmartPtr host (new VST3HostContext (nullptr)); MatchingDescriptionFinder finder (host, pluginFactory, description); const Result result (finder.findDescriptionsAndPerform (f)); if (result.getErrorMessage() == MatchingDescriptionFinder::getSuccessString()) { name = description.name; return true; } } return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3ModuleHandle) }; //============================================================================== class VST3PluginWindow : public AudioProcessorEditor, public ComponentMovementWatcher, public IPlugFrame { public: VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView) : AudioProcessorEditor (owner), ComponentMovementWatcher (this), refCount (1), view (pluginView, false), pluginHandle (nullptr), recursiveResize (false) { setSize (10, 10); setOpaque (true); setVisible (true); warnOnFailure (view->setFrame (this)); ViewRect rect; warnOnFailure (view->getSize (&rect)); resizeWithRect (*this, rect); } ~VST3PluginWindow() { warnOnFailure (view->removed()); warnOnFailure (view->setFrame (nullptr)); processor.editorBeingDeleted (this); #if JUCE_MAC dummyComponent.setView (nullptr); #endif view = nullptr; } JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS void paint (Graphics& g) override { g.fillAll (Colours::black); } void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel) override { view->onWheel (wheel.deltaY); } void focusGained (FocusChangeType) override { view->onFocus (true); } void focusLost (FocusChangeType) override { view->onFocus (false); } /** It seems that most, if not all, plugins do their own keyboard hooks, but IPlugView does have a set of keyboard related methods... */ bool keyStateChanged (bool /*isKeyDown*/) override { return true; } bool keyPressed (const KeyPress& /*key*/) override { return true; } //============================================================================== void componentMovedOrResized (bool, bool wasResized) override { if (recursiveResize) return; Component* const topComp = getTopLevelComponent(); if (topComp->getPeer() != nullptr) { #if JUCE_WINDOWS const Point pos (topComp->getLocalPoint (this, Point())); #endif recursiveResize = true; ViewRect rect; if (wasResized && view->canResize() == kResultTrue) { rect.right = (Steinberg::int32) getWidth(); rect.bottom = (Steinberg::int32) getHeight(); view->checkSizeConstraint (&rect); setSize ((int) rect.getWidth(), (int) rect.getHeight()); #if JUCE_WINDOWS SetWindowPos (pluginHandle, 0, pos.x, pos.y, rect.getWidth(), rect.getHeight(), isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); #elif JUCE_MAC dummyComponent.setBounds (getLocalBounds()); #endif view->onSize (&rect); } else { warnOnFailure (view->getSize (&rect)); #if JUCE_WINDOWS SetWindowPos (pluginHandle, 0, pos.x, pos.y, rect.getWidth(), rect.getHeight(), isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); #elif JUCE_MAC dummyComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight()); #endif } // Some plugins don't update their cursor correctly when mousing out the window Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); recursiveResize = false; } } void componentPeerChanged() override { } void componentVisibilityChanged() override { attachPluginWindow(); componentMovedOrResized (true, true); } tresult PLUGIN_API resizeView (IPlugView* incomingView, ViewRect* newSize) override { if (incomingView != nullptr && newSize != nullptr && incomingView == view) { resizeWithRect (dummyComponent, *newSize); setSize (dummyComponent.getWidth(), dummyComponent.getHeight()); return kResultTrue; } jassertfalse; return kInvalidArgument; } private: //============================================================================== Atomic refCount; ComSmartPtr view; #if JUCE_WINDOWS class ChildComponent : public Component { public: ChildComponent() {} void paint (Graphics& g) override { g.fillAll (Colours::cornflowerblue); } using Component::createNewPeer; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildComponent) }; ChildComponent dummyComponent; ScopedPointer peer; typedef HWND HandleFormat; #elif JUCE_MAC AutoResizingNSViewComponentWithParent dummyComponent; typedef NSView* HandleFormat; #else Component dummyComponent; typedef void* HandleFormat; #endif HandleFormat pluginHandle; bool recursiveResize; //============================================================================== static void resizeWithRect (Component& comp, const ViewRect& rect) { comp.setBounds ((int) rect.left, (int) rect.top, jmax (10, std::abs ((int) rect.getWidth())), jmax (10, std::abs ((int) rect.getHeight()))); } void attachPluginWindow() { if (pluginHandle == nullptr) { #if JUCE_WINDOWS if (Component* topComp = getTopLevelComponent()) peer = dummyComponent.createNewPeer (0, topComp->getWindowHandle()); else peer = nullptr; if (peer != nullptr) pluginHandle = (HandleFormat) peer->getNativeHandle(); #elif JUCE_MAC dummyComponent.setBounds (getLocalBounds()); addAndMakeVisible (dummyComponent); pluginHandle = (NSView*) dummyComponent.getView(); jassert (pluginHandle != nil); #endif if (pluginHandle != nullptr) warnOnFailure (view->attached (pluginHandle, defaultVST3WindowType)); } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow) }; //============================================================================== class VST3PluginInstance : public AudioPluginInstance { public: VST3PluginInstance (const VST3ModuleHandle::Ptr& handle) : module (handle), numInputAudioBusses (0), numOutputAudioBusses (0), inputParameterChanges (new ParamValueQueueList()), outputParameterChanges (new ParamValueQueueList()), midiInputs (new MidiEventList()), midiOutputs (new MidiEventList()), isComponentInitialised (false), isControllerInitialised (false), isActive (false) { host = new VST3HostContext (this); } ~VST3PluginInstance() { jassert (getActiveEditor() == nullptr); // You must delete any editors before deleting the plugin instance! releaseResources(); if (editControllerConnection != nullptr && componentConnection != nullptr) { editControllerConnection->disconnect (componentConnection); componentConnection->disconnect (editControllerConnection); } editController->setComponentHandler (nullptr); if (isControllerInitialised) editController->terminate(); if (isComponentInitialised) component->terminate(); componentConnection = nullptr; editControllerConnection = nullptr; unitData = nullptr; unitInfo = nullptr; programListData = nullptr; componentHandler2 = nullptr; componentHandler = nullptr; processor = nullptr; editController2 = nullptr; editController = nullptr; component = nullptr; host = nullptr; module = nullptr; } bool initialise() { #if JUCE_WINDOWS // On Windows it's highly advisable to create your plugins using the message thread, // because many plugins need a chance to create HWNDs that will get their messages // delivered by the main message thread, and that's not possible from a background thread. jassert (MessageManager::getInstance()->isThisTheMessageThread()); #endif ComSmartPtr factory (module->getPluginFactory()); PFactoryInfo factoryInfo; factory->getFactoryInfo (&factoryInfo); company = toString (factoryInfo.vendor).trim(); if (! fetchComponentAndController (factory, factory->countClasses())) return false; // (May return an error if the plugin combines the IComponent and IEditController implementations) editController->initialize (host->getFUnknown()); isControllerInitialised = true; editController->setComponentHandler (host); grabInformationObjects(); synchroniseStates(); interconnectComponentAndController(); setupIO(); return true; } //============================================================================== void fillInPluginDescription (PluginDescription& description) const override { jassert (module != nullptr); createPluginDescription (description, module->file, company, module->name, *info, info2, infoW, getNumInputChannels(), getNumOutputChannels()); } void* getPlatformSpecificData() override { return component; } void refreshParameterList() override {} //============================================================================== const String getName() const override { return module != nullptr ? module->name : String::empty; } void repopulateArrangements() { inputArrangements.clearQuick(); outputArrangements.clearQuick(); // NB: Some plugins need a valid arrangement despite specifying 0 for their I/O busses for (int i = 0; i < jmax (1, numInputAudioBusses); ++i) inputArrangements.add (getArrangementForBus (processor, true, i)); for (int i = 0; i < jmax (1, numOutputAudioBusses); ++i) outputArrangements.add (getArrangementForBus (processor, false, i)); } void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override { // Avoid redundantly calling things like setActive, which can be a heavy-duty call for some plugins: if (isActive && getSampleRate() == sampleRate && getBlockSize() == estimatedSamplesPerBlock) return; using namespace Vst; ProcessSetup setup; setup.symbolicSampleSize = kSample32; setup.maxSamplesPerBlock = estimatedSamplesPerBlock; setup.sampleRate = sampleRate; setup.processMode = isNonRealtime() ? kOffline : kRealtime; warnOnFailure (processor->setupProcessing (setup)); if (! isComponentInitialised) isComponentInitialised = component->initialize (host->getFUnknown()) == kResultTrue; editController->setComponentHandler (host); if (inputArrangements.size() <= 0 || outputArrangements.size() <= 0) repopulateArrangements(); warnOnFailure (processor->setBusArrangements (inputArrangements.getRawDataPointer(), numInputAudioBusses, outputArrangements.getRawDataPointer(), numOutputAudioBusses)); // Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!): const int newNumInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); const int newNumOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); // Repopulate arrangements if the number of busses have changed: if (numInputAudioBusses != newNumInputAudioBusses || numOutputAudioBusses != newNumOutputAudioBusses) { numInputAudioBusses = newNumInputAudioBusses; numOutputAudioBusses = newNumOutputAudioBusses; repopulateArrangements(); } // Needed for having the same sample rate in processBlock(); some plugins need this! setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), getNumSingleDirectionChannelsFor (component, false, true), sampleRate, estimatedSamplesPerBlock); setStateForAllBusses (true); setLatencySamples (jmax (0, (int) processor->getLatencySamples())); warnOnFailure (component->setActive (true)); warnOnFailure (processor->setProcessing (true)); isActive = true; } void releaseResources() override { if (! isActive) return; // Avoids redundantly calling things like setActive JUCE_TRY { isActive = false; setStateForAllBusses (false); if (processor != nullptr) warnOnFailure (processor->setProcessing (false)); if (component != nullptr) warnOnFailure (component->setActive (false)); } JUCE_CATCH_ALL_ASSERT } void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override { using namespace Vst; if (isActive && processor != nullptr && processor->canProcessSampleSize (kSample32) == kResultTrue) { const int numSamples = buffer.getNumSamples(); ProcessData data; data.processMode = isNonRealtime() ? kOffline : kRealtime; data.symbolicSampleSize = kSample32; data.numInputs = numInputAudioBusses; data.numOutputs = numOutputAudioBusses; data.inputParameterChanges = inputParameterChanges; data.outputParameterChanges = outputParameterChanges; data.numSamples = (Steinberg::int32) numSamples; updateTimingInformation (data, getSampleRate()); for (int i = getNumInputChannels(); i < buffer.getNumChannels(); ++i) buffer.clear (i, 0, numSamples); associateTo (data, buffer); associateTo (data, midiMessages); processor->process (data); MidiEventList::toMidiBuffer (midiMessages, *midiOutputs); inputParameterChanges->clearAllQueues(); } } //============================================================================== String getChannelName (int channelIndex, bool forInput, bool forAudioChannel) const { const int numBusses = getNumSingleDirectionBussesFor (component, forInput, forAudioChannel); int numCountedChannels = 0; for (int i = 0; i < numBusses; ++i) { Vst::BusInfo busInfo (getBusInfo (forInput, forAudioChannel, i)); numCountedChannels += busInfo.channelCount; if (channelIndex < numCountedChannels) return toString (busInfo.name); } return String::empty; } const String getInputChannelName (int channelIndex) const override { return getChannelName (channelIndex, true, true); } const String getOutputChannelName (int channelIndex) const override { return getChannelName (channelIndex, false, true); } bool isInputChannelStereoPair (int channelIndex) const override { if (channelIndex < 0 || channelIndex >= getNumInputChannels()) return false; return getBusInfo (true, true).channelCount == 2; } bool isOutputChannelStereoPair (int channelIndex) const override { if (channelIndex < 0 || channelIndex >= getNumOutputChannels()) return false; return getBusInfo (false, true).channelCount == 2; } bool acceptsMidi() const override { return getBusInfo (true, false).channelCount > 0; } bool producesMidi() const override { return getBusInfo (false, false).channelCount > 0; } //============================================================================== bool silenceInProducesSilenceOut() const override { if (processor != nullptr) return processor->getTailSamples() == Vst::kNoTail; return true; } /** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */ double getTailLengthSeconds() const override { if (processor != nullptr) { const double sampleRate = getSampleRate(); if (sampleRate > 0.0) return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate; } return 0.0; } //============================================================================== AudioProcessorEditor* createEditor() override { if (IPlugView* view = tryCreatingView()) return new VST3PluginWindow (this, view); return nullptr; } bool hasEditor() const override { // (if possible, avoid creating a second instance of the editor, because that crashes some plugins) if (getActiveEditor() != nullptr) return true; ComSmartPtr view (tryCreatingView(), false); return view != nullptr; } //============================================================================== int getNumParameters() override { if (editController != nullptr) return (int) editController->getParameterCount(); return 0; } const String getParameterName (int parameterIndex) override { return toString (getParameterInfoForIndex (parameterIndex).title); } float getParameter (int parameterIndex) override { if (editController != nullptr) { const uint32 id = getParameterInfoForIndex (parameterIndex).id; return (float) editController->getParamNormalized (id); } return 0.0f; } const String getParameterText (int parameterIndex) override { if (editController != nullptr) { const uint32 id = getParameterInfoForIndex (parameterIndex).id; Vst::String128 result; warnOnFailure (editController->getParamStringByValue (id, editController->getParamNormalized (id), result)); return toString (result); } return String::empty; } void setParameter (int parameterIndex, float newValue) override { if (editController != nullptr) { const uint32 paramID = getParameterInfoForIndex (parameterIndex).id; editController->setParamNormalized (paramID, (double) newValue); Steinberg::int32 index; inputParameterChanges->addParameterData (paramID, index)->addPoint (0, newValue, index); } } //============================================================================== int getNumPrograms() override { return getProgramListInfo (0).programCount; } int getCurrentProgram() override { return 0; } void setCurrentProgram (int) override {} void changeProgramName (int, const String&) override {} const String getProgramName (int index) override { Vst::String128 result; unitInfo->getProgramName (getProgramListInfo (0).id, index, result); return toString (result); } //============================================================================== void reset() override { if (component != nullptr) { component->setActive (false); component->setActive (true); } } //============================================================================== void getStateInformation (MemoryBlock& destData) override { XmlElement state ("VST3PluginState"); appendStateFrom (state, component, "IComponent"); appendStateFrom (state, editController, "IEditController"); AudioProcessor::copyXmlToBinary (state, destData); } void setStateInformation (const void* data, int sizeInBytes) override { ScopedPointer head (AudioProcessor::getXmlFromBinary (data, sizeInBytes)); if (head != nullptr) { ComSmartPtr s (createMemoryStreamForState (*head, "IComponent")); if (s != nullptr && component != nullptr) component->setState (s); if (editController != nullptr) { if (s != nullptr) editController->setComponentState (s); s = createMemoryStreamForState (*head, "IEditController"); if (s != nullptr) editController->setState (s); } } } /** @note Not applicable to VST3 */ void getCurrentProgramStateInformation (MemoryBlock& destData) override { destData.setSize (0, true); } /** @note Not applicable to VST3 */ void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override { (void) data; (void) sizeInBytes; } //============================================================================== // NB: this class and its subclasses must be public to avoid problems in // DLL builds under MSVC. class ParamValueQueueList : public Vst::IParameterChanges { public: ParamValueQueueList() {} virtual ~ParamValueQueueList() {} JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS Steinberg::int32 PLUGIN_API getParameterCount() override { return (Steinberg::int32) queues.size(); } Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32 index) override { return queues[(int) index]; } Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID& id, Steinberg::int32& index) override { for (int i = queues.size(); --i >= 0;) { if (queues.getUnchecked (i)->getParameterId() == id) { index = (Steinberg::int32) i; return queues.getUnchecked (i); } } index = getParameterCount(); return queues.add (new ParamValueQueue (id)); } void clearAllQueues() noexcept { for (int i = queues.size(); --i >= 0;) queues.getUnchecked (i)->clear(); } struct ParamValueQueue : public Vst::IParamValueQueue { ParamValueQueue (Vst::ParamID parameterID) : paramID (parameterID) { points.ensureStorageAllocated (1024); } virtual ~ParamValueQueue() {} JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS Steinberg::Vst::ParamID PLUGIN_API getParameterId() override { return paramID; } Steinberg::int32 PLUGIN_API getPointCount() override { return (Steinberg::int32) points.size(); } Steinberg::tresult PLUGIN_API getPoint (Steinberg::int32 index, Steinberg::int32& sampleOffset, Steinberg::Vst::ParamValue& value) override { const ScopedLock sl (pointLock); if (isPositiveAndBelow ((int) index, points.size())) { ParamPoint e (points.getUnchecked ((int) index)); sampleOffset = e.sampleOffset; value = e.value; return kResultTrue; } sampleOffset = -1; value = 0.0; return kResultFalse; } Steinberg::tresult PLUGIN_API addPoint (Steinberg::int32 sampleOffset, Steinberg::Vst::ParamValue value, Steinberg::int32& index) override { ParamPoint p = { sampleOffset, value }; const ScopedLock sl (pointLock); index = (Steinberg::int32) points.size(); points.add (p); return kResultTrue; } void clear() noexcept { const ScopedLock sl (pointLock); points.clearQuick(); } private: struct ParamPoint { Steinberg::int32 sampleOffset; Steinberg::Vst::ParamValue value; }; Atomic refCount; const Vst::ParamID paramID; Array points; CriticalSection pointLock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamValueQueue) }; Atomic refCount; OwnedArray queues; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamValueQueueList) }; private: //============================================================================== VST3ModuleHandle::Ptr module; friend VST3HostContext; ComSmartPtr host; // Information objects: String company; ScopedPointer info; ScopedPointer info2; ScopedPointer infoW; // Rudimentary interfaces: ComSmartPtr component; ComSmartPtr editController; ComSmartPtr editController2; ComSmartPtr processor; ComSmartPtr componentHandler; ComSmartPtr componentHandler2; ComSmartPtr unitInfo; ComSmartPtr unitData; ComSmartPtr programListData; ComSmartPtr componentConnection; ComSmartPtr editControllerConnection; /** The number of IO busses MUST match that of the plugin, even if there aren't enough channels to process, as very poorly specified by the Steinberg SDK */ int numInputAudioBusses, numOutputAudioBusses; Array inputArrangements, outputArrangements; // Caching to improve performance and to avoid possible non-thread-safe calls to getBusArrangements(). VST3BufferExchange::BusMap inputBusMap, outputBusMap; Array inputBusses, outputBusses; //============================================================================== template static void appendStateFrom (XmlElement& head, ComSmartPtr& object, const String& identifier) { if (object != nullptr) { Steinberg::MemoryStream stream; if (object->getState (&stream) == kResultTrue) { MemoryBlock info (stream.getData(), (size_t) stream.getSize()); head.createNewChildElement (identifier)->addTextElement (info.toBase64Encoding()); } } } static Steinberg::MemoryStream* createMemoryStreamForState (XmlElement& head, StringRef identifier) { Steinberg::MemoryStream* stream = nullptr; if (XmlElement* const state = head.getChildByName (identifier)) { MemoryBlock mem; if (mem.fromBase64Encoding (state->getAllSubText())) { stream = new Steinberg::MemoryStream(); stream->setSize ((TSize) mem.getSize()); mem.copyTo (stream->getData(), 0, mem.getSize()); } } return stream; } ComSmartPtr inputParameterChanges, outputParameterChanges; ComSmartPtr midiInputs, midiOutputs; Vst::ProcessContext timingInfo; //< Only use this in processBlock()! bool isComponentInitialised, isControllerInitialised, isActive; //============================================================================== bool fetchComponentAndController (IPluginFactory* factory, const Steinberg::int32 numClasses) { jassert (numClasses >= 0); // The plugin must provide at least an IComponent and IEditController! for (Steinberg::int32 j = 0; j < numClasses; ++j) { info = new PClassInfo(); factory->getClassInfo (j, info); if (std::strcmp (info->category, kVstAudioEffectClass) != 0) continue; const String name (toString (info->name).trim()); if (module->name != name) continue; { ComSmartPtr pf2; ComSmartPtr pf3; if (pf2.loadFrom (factory)) { info2 = new PClassInfo2(); pf2->getClassInfo2 (j, info2); } else { info2 = nullptr; } if (pf3.loadFrom (factory)) { pf3->setHostContext (host->getFUnknown()); infoW = new PClassInfoW(); pf3->getClassInfoUnicode (j, infoW); } else { infoW = nullptr; } } bool failed = true; if (component.loadFrom (factory, info->cid) && component != nullptr) { warnOnFailure (component->setIoMode (isNonRealtime() ? Vst::kOffline : Vst::kRealtime)); if (warnOnFailure (component->initialize (host->getFUnknown())) != kResultOk) return false; isComponentInitialised = true; // Get the IEditController: TUID controllerCID = { 0 }; if (component->getControllerClassId (controllerCID) == kResultTrue && FUID (controllerCID).isValid()) editController.loadFrom (factory, controllerCID); if (editController == nullptr) { // Try finding the IEditController the long way around: for (Steinberg::int32 i = 0; i < numClasses; ++i) { PClassInfo classInfo; factory->getClassInfo (i, &classInfo); if (std::strcmp (classInfo.category, kVstComponentControllerClass) == 0) editController.loadFrom (factory, classInfo.cid); } } if (editController == nullptr) editController.loadFrom (component); failed = editController == nullptr; } if (failed) { jassertfalse; // The plugin won't function without a valid IComponent and IEditController implementation! if (component != nullptr) { component->terminate(); component = nullptr; } if (editController != nullptr) { editController->terminate(); editController = nullptr; } break; } return true; } return false; } /** Some plugins need to be "connected" to intercommunicate between their implemented classes */ void interconnectComponentAndController() { componentConnection.loadFrom (component); editControllerConnection.loadFrom (editController); if (componentConnection != nullptr && editControllerConnection != nullptr) { warnOnFailure (editControllerConnection->connect (componentConnection)); warnOnFailure (componentConnection->connect (editControllerConnection)); } } void synchroniseStates() { Steinberg::MemoryStream stream; if (component->getState (&stream) == kResultTrue) if (stream.seek (0, Steinberg::IBStream::kIBSeekSet, nullptr) == kResultTrue) warnOnFailure (editController->setComponentState (&stream)); } void grabInformationObjects() { processor.loadFrom (component); unitInfo.loadFrom (component); programListData.loadFrom (component); unitData.loadFrom (component); editController2.loadFrom (component); componentHandler.loadFrom (component); componentHandler2.loadFrom (component); if (processor == nullptr) processor.loadFrom (editController); if (unitInfo == nullptr) unitInfo.loadFrom (editController); if (programListData == nullptr) programListData.loadFrom (editController); if (unitData == nullptr) unitData.loadFrom (editController); if (editController2 == nullptr) editController2.loadFrom (editController); if (componentHandler == nullptr) componentHandler.loadFrom (editController); if (componentHandler2 == nullptr) componentHandler2.loadFrom (editController); } void setStateForAllBusses (bool newState) { setStateForAllBussesOfType (component, newState, true, true); // Activate/deactivate audio inputs setStateForAllBussesOfType (component, newState, false, true); // Activate/deactivate audio outputs setStateForAllBussesOfType (component, newState, true, false); // Activate/deactivate MIDI inputs setStateForAllBussesOfType (component, newState, false, false); // Activate/deactivate MIDI outputs } void setupIO() { setStateForAllBusses (true); Vst::ProcessSetup setup; setup.symbolicSampleSize = Vst::kSample32; setup.maxSamplesPerBlock = 1024; setup.sampleRate = 44100.0; setup.processMode = Vst::kRealtime; warnOnFailure (processor->setupProcessing (setup)); numInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); numOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), getNumSingleDirectionChannelsFor (component, false, true), setup.sampleRate, (int) setup.maxSamplesPerBlock); } //============================================================================== Vst::BusInfo getBusInfo (bool forInput, bool forAudio, int index = 0) const { Vst::BusInfo busInfo; busInfo.mediaType = forAudio ? Vst::kAudio : Vst::kEvent; busInfo.direction = forInput ? Vst::kInput : Vst::kOutput; component->getBusInfo (busInfo.mediaType, busInfo.direction, (Steinberg::int32) index, busInfo); return busInfo; } //============================================================================== /** @note An IPlugView, when first created, should start with a ref-count of 1! */ IPlugView* tryCreatingView() const { IPlugView* v = editController->createView (Vst::ViewType::kEditor); if (v == nullptr) v = editController->createView (nullptr); if (v == nullptr) editController->queryInterface (IPlugView::iid, (void**) &v); return v; } //============================================================================== void associateTo (Vst::ProcessData& destination, AudioSampleBuffer& buffer) { using namespace VST3BufferExchange; mapBufferToBusses (inputBusses, inputBusMap, inputArrangements, buffer); mapBufferToBusses (outputBusses, outputBusMap, outputArrangements, buffer); destination.inputs = inputBusses.getRawDataPointer(); destination.outputs = outputBusses.getRawDataPointer(); } void associateTo (Vst::ProcessData& destination, MidiBuffer& midiBuffer) { midiInputs->clear(); midiOutputs->clear(); MidiEventList::toEventList (*midiInputs, midiBuffer); destination.inputEvents = midiInputs; destination.outputEvents = midiOutputs; } void updateTimingInformation (Vst::ProcessData& destination, double processSampleRate) { toProcessContext (timingInfo, getPlayHead(), processSampleRate); destination.processContext = &timingInfo; } Vst::ParameterInfo getParameterInfoForIndex (int index) const { Vst::ParameterInfo paramInfo = { 0 }; if (processor != nullptr) editController->getParameterInfo (index, paramInfo); return paramInfo; } Vst::ProgramListInfo getProgramListInfo (int index) const { Vst::ProgramListInfo paramInfo = { 0 }; if (unitInfo != nullptr) unitInfo->getProgramListInfo (index, paramInfo); return paramInfo; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginInstance) }; }; //============================================================================== VST3PluginFormat::VST3PluginFormat() {} VST3PluginFormat::~VST3PluginFormat() {} void VST3PluginFormat::findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) { if (! fileMightContainThisPluginType (fileOrIdentifier)) return; VST3Classes::VST3ModuleHandle::getAllDescriptionsForFile (results, fileOrIdentifier); } AudioPluginInstance* VST3PluginFormat::createInstanceFromDescription (const PluginDescription& description, double, int) { ScopedPointer result; if (fileMightContainThisPluginType (description.fileOrIdentifier)) { File file (description.fileOrIdentifier); const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); file.getParentDirectory().setAsCurrentWorkingDirectory(); if (const VST3Classes::VST3ModuleHandle::Ptr module = VST3Classes::VST3ModuleHandle::findOrCreateModule (file, description)) { result = new VST3Classes::VST3PluginInstance (module); if (! result->initialise()) result = nullptr; } previousWorkingDirectory.setAsCurrentWorkingDirectory(); } return result.release(); } bool VST3PluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); return f.hasFileExtension (".vst3") #if JUCE_MAC && f.exists(); #else && f.existsAsFile(); #endif } String VST3PluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; //Impossible to tell because every VST3 is a type of shell... } bool VST3PluginFormat::pluginNeedsRescanning (const PluginDescription& description) { return File (description.fileOrIdentifier).getLastModificationTime() != description.lastFileModTime; } bool VST3PluginFormat::doesPluginStillExist (const PluginDescription& description) { return File (description.fileOrIdentifier).exists(); } StringArray VST3PluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive) { StringArray results; for (int i = 0; i < directoriesToSearch.getNumPaths(); ++i) recursiveFileSearch (results, directoriesToSearch[i], recursive); return results; } void VST3PluginFormat::recursiveFileSearch (StringArray& results, const File& directory, const bool recursive) { DirectoryIterator iter (directory, false, "*", File::findFilesAndDirectories); while (iter.next()) { const File f (iter.getFile()); bool isPlugin = false; if (fileMightContainThisPluginType (f.getFullPathName())) { isPlugin = true; results.add (f.getFullPathName()); } if (recursive && (! isPlugin) && f.isDirectory()) recursiveFileSearch (results, f, true); } } FileSearchPath VST3PluginFormat::getDefaultLocationsToSearch() { #if JUCE_WINDOWS const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); return FileSearchPath (programFiles + "\\Common Files\\VST3"); #elif JUCE_MAC return FileSearchPath ("/Library/Audio/Plug-Ins/VST3;~/Library/Audio/Plug-Ins/VST3"); #else return FileSearchPath(); #endif } #endif //JUCE_PLUGINHOST_VST3 juce_VST3PluginFormat.h000066400000000000000000000054161320201440200351060ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_VST3PLUGINFORMAT_H_INCLUDED #define JUCE_VST3PLUGINFORMAT_H_INCLUDED #if JUCE_PLUGINHOST_VST3 /** Implements a plugin format for VST3s. */ class JUCE_API VST3PluginFormat : public AudioPluginFormat { public: /** Constructor */ VST3PluginFormat(); /** Destructor */ ~VST3PluginFormat(); //============================================================================== /** @internal */ String getName() const override { return "VST3"; } /** @internal */ void findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) override; /** @internal */ AudioPluginInstance* createInstanceFromDescription (const PluginDescription& description, double, int) override; /** @internal */ bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; /** @internal */ String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; /** @internal */ bool pluginNeedsRescanning (const PluginDescription& description) override; /** @internal */ StringArray searchPathsForPlugins (const FileSearchPath& searchPath, bool recursive) override; /** @internal */ bool doesPluginStillExist (const PluginDescription& description) override; /** @internal */ FileSearchPath getDefaultLocationsToSearch() override; /** @internal */ bool canScanForPlugins() const override { return true; } private: //============================================================================== void recursiveFileSearch (StringArray&, const File&, bool recursive); //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat) }; #endif // JUCE_PLUGINHOST_VST3 #endif // JUCE_VST3PLUGINFORMAT_H_INCLUDED juce_VSTMidiEventList.h000066400000000000000000000140161320201440200351300ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifdef __aeffect__ // NB: this must come first, *before* the header-guard. #ifndef JUCE_VSTMIDIEVENTLIST_H_INCLUDED #define JUCE_VSTMIDIEVENTLIST_H_INCLUDED //============================================================================== /** Holds a set of VSTMidiEvent objects and makes it easy to add events to the list. This is used by both the VST hosting code and the plugin wrapper. */ class VSTMidiEventList { public: //============================================================================== VSTMidiEventList() : numEventsUsed (0), numEventsAllocated (0) { } ~VSTMidiEventList() { freeEvents(); } //============================================================================== void clear() { numEventsUsed = 0; if (events != nullptr) events->numEvents = 0; } void addEvent (const void* const midiData, const int numBytes, const int frameOffset) { ensureSize (numEventsUsed + 1); VstMidiEvent* const e = (VstMidiEvent*) (events->events [numEventsUsed]); events->numEvents = ++numEventsUsed; if (numBytes <= 4) { if (e->type == kVstSysExType) { delete[] (((VstMidiSysexEvent*) e)->sysexDump); e->type = kVstMidiType; e->byteSize = sizeof (VstMidiEvent); e->noteLength = 0; e->noteOffset = 0; e->detune = 0; e->noteOffVelocity = 0; } e->deltaFrames = frameOffset; memcpy (e->midiData, midiData, (size_t) numBytes); } else { VstMidiSysexEvent* const se = (VstMidiSysexEvent*) e; if (se->type == kVstSysExType) delete[] se->sysexDump; se->sysexDump = new char [numBytes]; memcpy (se->sysexDump, midiData, (size_t) numBytes); se->type = kVstSysExType; se->byteSize = sizeof (VstMidiSysexEvent); se->deltaFrames = frameOffset; se->flags = 0; se->dumpBytes = numBytes; se->resvd1 = 0; se->resvd2 = 0; } } //============================================================================== // Handy method to pull the events out of an event buffer supplied by the host // or plugin. static void addEventsToMidiBuffer (const VstEvents* events, MidiBuffer& dest) { for (int i = 0; i < events->numEvents; ++i) { const VstEvent* const e = events->events[i]; if (e != nullptr) { if (e->type == kVstMidiType) { dest.addEvent ((const juce::uint8*) ((const VstMidiEvent*) e)->midiData, 4, e->deltaFrames); } else if (e->type == kVstSysExType) { dest.addEvent ((const juce::uint8*) ((const VstMidiSysexEvent*) e)->sysexDump, (int) ((const VstMidiSysexEvent*) e)->dumpBytes, e->deltaFrames); } } } } //============================================================================== void ensureSize (int numEventsNeeded) { if (numEventsNeeded > numEventsAllocated) { numEventsNeeded = (numEventsNeeded + 32) & ~31; const size_t size = 20 + sizeof (VstEvent*) * (size_t) numEventsNeeded; if (events == nullptr) events.calloc (size, 1); else events.realloc (size, 1); for (int i = numEventsAllocated; i < numEventsNeeded; ++i) events->events[i] = allocateVSTEvent(); numEventsAllocated = numEventsNeeded; } } void freeEvents() { if (events != nullptr) { for (int i = numEventsAllocated; --i >= 0;) freeVSTEvent (events->events[i]); events.free(); numEventsUsed = 0; numEventsAllocated = 0; } } //============================================================================== HeapBlock events; private: int numEventsUsed, numEventsAllocated; static VstEvent* allocateVSTEvent() { VstEvent* const e = (VstEvent*) std::calloc (1, sizeof (VstMidiEvent) > sizeof (VstMidiSysexEvent) ? sizeof (VstMidiEvent) : sizeof (VstMidiSysexEvent)); e->type = kVstMidiType; e->byteSize = sizeof (VstMidiEvent); return e; } static void freeVSTEvent (VstEvent* e) { if (e->type == kVstSysExType) delete[] (((VstMidiSysexEvent*) e)->sysexDump); std::free (e); } }; #endif // JUCE_VSTMIDIEVENTLIST_H_INCLUDED #endif // JUCE_VSTMIDIEVENTLIST_H_INCLUDED juce_VSTPluginFormat.cpp000066400000000000000000002760101320201440200353560ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_PLUGINHOST_VST //============================================================================== #if JUCE_MAC && JUCE_SUPPORT_CARBON #include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h" #endif //============================================================================== #undef PRAGMA_ALIGN_SUPPORTED #define VST_FORCE_DEPRECATED 0 #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4996) #elif ! JUCE_MINGW #define __cdecl #endif /* Obviously you're going to need the Steinberg vstsdk2.4 folder in your include path if you want to add VST support. If you're not interested in VSTs, you can disable them by setting the JUCE_PLUGINHOST_VST flag to 0. */ #include "pluginterfaces/vst2.x/aeffectx.h" #if JUCE_MSVC #pragma warning (pop) #pragma warning (disable: 4355) // ("this" used in initialiser list warning) #endif //============================================================================== #include "juce_VSTMidiEventList.h" #if JUCE_MINGW #ifndef WM_APPCOMMAND #define WM_APPCOMMAND 0x0319 #endif extern "C" void _fpreset(); extern "C" void _clearfp(); #elif ! JUCE_WINDOWS static void _fpreset() {} static void _clearfp() {} #endif #ifndef JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN #define JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN #endif #ifndef JUCE_VST_WRAPPER_INVOKE_MAIN #define JUCE_VST_WRAPPER_INVOKE_MAIN effect = module->moduleMain (&audioMaster); #endif //============================================================================== namespace { const int fxbVersionNum = 1; struct fxProgram { VstInt32 chunkMagic; // 'CcnK' VstInt32 byteSize; // of this chunk, excl. magic + byteSize VstInt32 fxMagic; // 'FxCk' VstInt32 version; VstInt32 fxID; // fx unique id VstInt32 fxVersion; VstInt32 numParams; char prgName[28]; float params[1]; // variable no. of parameters }; struct fxSet { VstInt32 chunkMagic; // 'CcnK' VstInt32 byteSize; // of this chunk, excl. magic + byteSize VstInt32 fxMagic; // 'FxBk' VstInt32 version; VstInt32 fxID; // fx unique id VstInt32 fxVersion; VstInt32 numPrograms; char future[128]; fxProgram programs[1]; // variable no. of programs }; struct fxChunkSet { VstInt32 chunkMagic; // 'CcnK' VstInt32 byteSize; // of this chunk, excl. magic + byteSize VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh' VstInt32 version; VstInt32 fxID; // fx unique id VstInt32 fxVersion; VstInt32 numPrograms; char future[128]; VstInt32 chunkSize; char chunk[8]; // variable }; struct fxProgramSet { VstInt32 chunkMagic; // 'CcnK' VstInt32 byteSize; // of this chunk, excl. magic + byteSize VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh' VstInt32 version; VstInt32 fxID; // fx unique id VstInt32 fxVersion; VstInt32 numPrograms; char name[28]; VstInt32 chunkSize; char chunk[8]; // variable }; // Compares a magic value in either endianness. static bool compareMagic (VstInt32 magic, const char* name) noexcept { return magic == (VstInt32) ByteOrder::littleEndianInt (name) || magic == (VstInt32) ByteOrder::bigEndianInt (name); } static VstInt32 fxbName (const char* name) noexcept { return (VstInt32) ByteOrder::littleEndianInt (name); } static VstInt32 fxbSwap (const VstInt32 x) noexcept { return (VstInt32) ByteOrder::swapIfLittleEndian ((uint32) x); } static float fxbSwapFloat (const float x) noexcept { #ifdef JUCE_LITTLE_ENDIAN union { uint32 asInt; float asFloat; } n; n.asFloat = x; n.asInt = ByteOrder::swap (n.asInt); return n.asFloat; #else return x; #endif } } //============================================================================== namespace { static double getVSTHostTimeNanoseconds() noexcept { #if JUCE_WINDOWS return timeGetTime() * 1000000.0; #elif JUCE_LINUX timeval micro; gettimeofday (µ, 0); return micro.tv_usec * 1000.0; #elif JUCE_MAC UnsignedWide micro; Microseconds (µ); return micro.lo * 1000.0; #endif } static int shellUIDToCreate = 0; static int insideVSTCallback = 0; struct IdleCallRecursionPreventer { IdleCallRecursionPreventer() : isMessageThread (MessageManager::getInstance()->isThisTheMessageThread()) { if (isMessageThread) ++insideVSTCallback; } ~IdleCallRecursionPreventer() { if (isMessageThread) --insideVSTCallback; } const bool isMessageThread; JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer) }; #if JUCE_MAC static bool makeFSRefFromPath (FSRef* destFSRef, const String& path) { return FSPathMakeRef (reinterpret_cast (path.toRawUTF8()), destFSRef, 0) == noErr; } #endif #if JUCE_MAC && JUCE_PPC static void* newCFMFromMachO (void* const machofp) noexcept { void* result = (void*) new char[8]; ((void**) result)[0] = machofp; ((void**) result)[1] = result; return result; } #endif } //============================================================================== typedef AEffect* (VSTCALLBACK *MainCall) (audioMasterCallback); static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); //============================================================================== // Change this to disable logging of various VST activities #ifndef VST_LOGGING #define VST_LOGGING 1 #endif #if VST_LOGGING #define JUCE_VST_LOG(a) Logger::writeToLog(a); #else #define JUCE_VST_LOG(a) #endif //============================================================================== #if JUCE_LINUX extern Display* display; extern XContext windowHandleXContext; namespace { static bool xErrorTriggered = false; static int temporaryErrorHandler (Display*, XErrorEvent*) { xErrorTriggered = true; return 0; } typedef void (*EventProcPtr) (XEvent* ev); static EventProcPtr getPropertyFromXWindow (Window handle, Atom atom) { XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); xErrorTriggered = false; int userSize; unsigned long bytes, userCount; unsigned char* data; Atom userType; XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType, &userType, &userSize, &userCount, &bytes, &data); XSetErrorHandler (oldErrorHandler); return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast (data) : nullptr; } Window getChildWindow (Window windowToCheck) { Window rootWindow, parentWindow; Window* childWindows; unsigned int numChildren = 0; XQueryTree (display, windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren); if (numChildren > 0) return childWindows [0]; return 0; } static void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) noexcept { if (e.mods.isLeftButtonDown()) { ev.xbutton.button = Button1; ev.xbutton.state |= Button1Mask; } else if (e.mods.isRightButtonDown()) { ev.xbutton.button = Button3; ev.xbutton.state |= Button3Mask; } else if (e.mods.isMiddleButtonDown()) { ev.xbutton.button = Button2; ev.xbutton.state |= Button2Mask; } } static void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) noexcept { if (e.mods.isLeftButtonDown()) ev.xmotion.state |= Button1Mask; else if (e.mods.isRightButtonDown()) ev.xmotion.state |= Button3Mask; else if (e.mods.isMiddleButtonDown()) ev.xmotion.state |= Button2Mask; } static void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) noexcept { if (e.mods.isLeftButtonDown()) ev.xcrossing.state |= Button1Mask; else if (e.mods.isRightButtonDown()) ev.xcrossing.state |= Button3Mask; else if (e.mods.isMiddleButtonDown()) ev.xcrossing.state |= Button2Mask; } static void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) noexcept { ignoreUnused (e); if (increment < 0) { ev.xbutton.button = Button5; ev.xbutton.state |= Button5Mask; } else if (increment > 0) { ev.xbutton.button = Button4; ev.xbutton.state |= Button4Mask; } } } #endif //============================================================================== class ModuleHandle : public ReferenceCountedObject { public: //============================================================================== File file; MainCall moduleMain, customMain; String pluginName; ScopedPointer vstXml; typedef ReferenceCountedObjectPtr Ptr; static Array& getActiveModules() { static Array activeModules; return activeModules; } //============================================================================== static ModuleHandle* findOrCreateModule (const File& file) { for (int i = getActiveModules().size(); --i >= 0;) { ModuleHandle* const module = getActiveModules().getUnchecked(i); if (module->file == file) return module; } _fpreset(); // (doesn't do any harm) const IdleCallRecursionPreventer icrp; shellUIDToCreate = 0; JUCE_VST_LOG ("Attempting to load VST: " + file.getFullPathName()); ScopedPointer m (new ModuleHandle (file)); if (! m->open()) m = nullptr; _fpreset(); // (doesn't do any harm) return m.release(); } //============================================================================== ModuleHandle (const File& f) : file (f), moduleMain (nullptr), customMain (nullptr) #if JUCE_MAC #if JUCE_PPC , fragId (0) #endif , resHandle (0), bundleRef (0), resFileId (0) #endif { getActiveModules().add (this); #if JUCE_WINDOWS || JUCE_LINUX fullParentDirectoryPathName = f.getParentDirectory().getFullPathName(); #elif JUCE_MAC FSRef ref; makeFSRefFromPath (&ref, f.getParentDirectory().getFullPathName()); FSGetCatalogInfo (&ref, kFSCatInfoNone, 0, 0, &parentDirFSSpec, 0); #endif } ~ModuleHandle() { getActiveModules().removeFirstMatchingValue (this); close(); } //============================================================================== #if JUCE_WINDOWS || JUCE_LINUX DynamicLibrary module; String fullParentDirectoryPathName; bool open() { pluginName = file.getFileNameWithoutExtension(); module.open (file.getFullPathName()); moduleMain = (MainCall) module.getFunction ("VSTPluginMain"); if (moduleMain == nullptr) moduleMain = (MainCall) module.getFunction ("main"); JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN if (moduleMain != nullptr) { vstXml = XmlDocument::parse (file.withFileExtension ("vstxml")); #if JUCE_WINDOWS if (vstXml == nullptr) vstXml = XmlDocument::parse (getDLLResource (file, "VSTXML", 1)); #endif } return moduleMain != nullptr; } void close() { _fpreset(); // (doesn't do any harm) module.close(); } void closeEffect (AEffect* eff) { eff->dispatcher (eff, effClose, 0, 0, 0, 0); } #if JUCE_WINDOWS static String getDLLResource (const File& dllFile, const String& type, int resID) { DynamicLibrary dll (dllFile.getFullPathName()); HMODULE dllModule = (HMODULE) dll.getNativeHandle(); if (dllModule != INVALID_HANDLE_VALUE) { if (HRSRC res = FindResource (dllModule, MAKEINTRESOURCE (resID), type.toWideCharPointer())) { if (HGLOBAL hGlob = LoadResource (dllModule, res)) { const char* data = static_cast (LockResource (hGlob)); return String::fromUTF8 (data, SizeofResource (dllModule, res)); } } } return String::empty; } #endif #else #if JUCE_PPC CFragConnectionID fragId; #endif Handle resHandle; CFBundleRef bundleRef; FSSpec parentDirFSSpec; short resFileId; bool open() { bool ok = false; if (file.hasFileExtension (".vst")) { const char* const utf8 = file.getFullPathName().toRawUTF8(); if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, (CFIndex) strlen (utf8), file.isDirectory())) { bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); if (bundleRef != 0) { if (CFBundleLoadExecutable (bundleRef)) { moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho")); if (moduleMain == nullptr) moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain")); JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN if (moduleMain != nullptr) { if (CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"))) { if (CFGetTypeID (name) == CFStringGetTypeID()) { char buffer[1024]; if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding())) pluginName = buffer; } } if (pluginName.isEmpty()) pluginName = file.getFileNameWithoutExtension(); resFileId = CFBundleOpenBundleResourceMap (bundleRef); ok = true; Array vstXmlFiles; file.getChildFile ("Contents") .getChildFile ("Resources") .findChildFiles (vstXmlFiles, File::findFiles, false, "*.vstxml"); if (vstXmlFiles.size() > 0) vstXml = XmlDocument::parse (vstXmlFiles.getReference(0)); } } if (! ok) { CFBundleUnloadExecutable (bundleRef); CFRelease (bundleRef); bundleRef = 0; } } } } #if JUCE_PPC else { FSRef fn; if (FSPathMakeRef ((UInt8*) file.getFullPathName().toRawUTF8(), &fn, 0) == noErr) { resFileId = FSOpenResFile (&fn, fsRdPerm); if (resFileId != -1) { const int numEffs = Count1Resources ('aEff'); for (int i = 0; i < numEffs; ++i) { resHandle = Get1IndResource ('aEff', i + 1); if (resHandle != 0) { OSType type; Str255 name; SInt16 id; GetResInfo (resHandle, &id, &type, name); pluginName = String ((const char*) name + 1, name[0]); DetachResource (resHandle); HLock (resHandle); ::Ptr ptr; Str255 errorText; OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle), name, kPrivateCFragCopy, &fragId, &ptr, errorText); if (err == noErr) { moduleMain = (MainCall) newMachOFromCFM (ptr); ok = true; } else { HUnlock (resHandle); } break; } } if (! ok) CloseResFile (resFileId); } } } #endif return ok; } void close() { #if JUCE_PPC if (fragId != 0) { if (moduleMain != nullptr) disposeMachOFromCFM ((void*) moduleMain); CloseConnection (&fragId); HUnlock (resHandle); if (resFileId != 0) CloseResFile (resFileId); } else #endif if (bundleRef != 0) { CFBundleCloseBundleResourceMap (bundleRef, resFileId); if (CFGetRetainCount (bundleRef) == 1) CFBundleUnloadExecutable (bundleRef); if (CFGetRetainCount (bundleRef) > 0) CFRelease (bundleRef); } } void closeEffect (AEffect* eff) { #if JUCE_PPC if (fragId != 0) { Array thingsToDelete; thingsToDelete.add ((void*) eff->dispatcher); thingsToDelete.add ((void*) eff->process); thingsToDelete.add ((void*) eff->setParameter); thingsToDelete.add ((void*) eff->getParameter); thingsToDelete.add ((void*) eff->processReplacing); eff->dispatcher (eff, effClose, 0, 0, 0, 0); for (int i = thingsToDelete.size(); --i >= 0;) disposeMachOFromCFM (thingsToDelete[i]); } else #endif { eff->dispatcher (eff, effClose, 0, 0, 0, 0); } } #if JUCE_PPC static void* newMachOFromCFM (void* cfmfp) { if (cfmfp == 0) return nullptr; UInt32* const mfp = new UInt32[6]; mfp[0] = 0x3d800000 | ((UInt32) cfmfp >> 16); mfp[1] = 0x618c0000 | ((UInt32) cfmfp & 0xffff); mfp[2] = 0x800c0000; mfp[3] = 0x804c0004; mfp[4] = 0x7c0903a6; mfp[5] = 0x4e800420; MakeDataExecutable (mfp, sizeof (UInt32) * 6); return mfp; } static void disposeMachOFromCFM (void* ptr) { delete[] static_cast (ptr); } void coerceAEffectFunctionCalls (AEffect* eff) { if (fragId != 0) { eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher); eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process); eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter); eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter); eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing); } } #endif #endif private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleHandle) }; static const int defaultVSTSampleRateValue = 44100; static const int defaultVSTBlockSizeValue = 512; //============================================================================== //============================================================================== class VSTPluginInstance : public AudioPluginInstance, private Timer, private AsyncUpdater { public: VSTPluginInstance (const ModuleHandle::Ptr& mh) : effect (nullptr), module (mh), usesCocoaNSView (false), name (mh->pluginName), wantsMidiMessages (false), initialised (false), isPowerOn (false), tempBuffer (1, 1) { try { const IdleCallRecursionPreventer icrp; _fpreset(); JUCE_VST_LOG ("Creating VST instance: " + name); #if JUCE_MAC if (module->resFileId != 0) UseResFile (module->resFileId); #if JUCE_PPC if (module->fragId != 0) { static void* audioMasterCoerced = nullptr; if (audioMasterCoerced == nullptr) audioMasterCoerced = newCFMFromMachO ((void*) &audioMaster); effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); } else #endif #endif { JUCE_VST_WRAPPER_INVOKE_MAIN } if (effect != nullptr && effect->magic == kEffectMagic) { #if JUCE_PPC module->coerceAEffectFunctionCalls (effect); #endif jassert (effect->resvd2 == 0); jassert (effect->object != 0); _fpreset(); // some dodgy plugs fuck around with this } else { effect = nullptr; } } catch (...) {} } ~VSTPluginInstance() { const ScopedLock sl (lock); if (effect != nullptr && effect->magic == kEffectMagic) { #if JUCE_MAC if (module->resFileId != 0) UseResFile (module->resFileId); #endif // Must delete any editors before deleting the plugin instance! jassert (getActiveEditor() == 0); _fpreset(); // some dodgy plugs fuck around with this module->closeEffect (effect); } module = nullptr; effect = nullptr; } void fillInPluginDescription (PluginDescription& desc) const override { desc.name = name; { char buffer [512] = { 0 }; dispatch (effGetEffectName, 0, 0, buffer, 0); desc.descriptiveName = String::fromUTF8 (buffer).trim(); if (desc.descriptiveName.isEmpty()) desc.descriptiveName = name; } desc.fileOrIdentifier = module->file.getFullPathName(); desc.uid = getUID(); desc.lastFileModTime = module->file.getLastModificationTime(); desc.pluginFormatName = "VST"; desc.category = getCategory(); { char buffer [kVstMaxVendorStrLen + 8] = { 0 }; dispatch (effGetVendorString, 0, 0, buffer, 0); desc.manufacturerName = String::fromUTF8 (buffer); } desc.version = getVersion(); desc.numInputChannels = getNumInputChannels(); desc.numOutputChannels = getNumOutputChannels(); desc.isInstrument = (effect != nullptr && (effect->flags & effFlagsIsSynth) != 0); } void initialise (double initialSampleRate, int initialBlockSize) { if (initialised || effect == nullptr) return; #if JUCE_WINDOWS // On Windows it's highly advisable to create your plugins using the message thread, // because many plugins need a chance to create HWNDs that will get their // messages delivered by the main message thread, and that's not possible from // a background thread. jassert (MessageManager::getInstance()->isThisTheMessageThread()); #endif JUCE_VST_LOG ("Initialising VST: " + module->pluginName + " (" + getVersion() + ")"); initialised = true; setPlayConfigDetails (effect->numInputs, effect->numOutputs, initialSampleRate, initialBlockSize); dispatch (effIdentify, 0, 0, 0, 0); if (getSampleRate() > 0) dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); if (getBlockSize() > 0) dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); dispatch (effOpen, 0, 0, 0, 0); setPlayConfigDetails (effect->numInputs, effect->numOutputs, getSampleRate(), getBlockSize()); if (getNumPrograms() > 1) setCurrentProgram (0); else dispatch (effSetProgram, 0, 0, 0, 0); for (int i = effect->numInputs; --i >= 0;) dispatch (effConnectInput, i, 1, 0, 0); for (int i = effect->numOutputs; --i >= 0;) dispatch (effConnectOutput, i, 1, 0, 0); if (getVstCategory() != kPlugCategShell) // (workaround for Waves 5 plugins which crash during this call) updateStoredProgramNames(); wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; #if JUCE_MAC && JUCE_SUPPORT_CARBON usesCocoaNSView = (dispatch (effCanDo, 0, 0, (void*) "hasCockosViewAsConfig", 0) & (int) 0xffff0000) == 0xbeef0000; #endif setLatencySamples (effect->initialDelay); } void* getPlatformSpecificData() override { return effect; } const String getName() const override { return name; } int getUID() const { int uid = effect != nullptr ? effect->uniqueID : 0; if (uid == 0) uid = module->file.hashCode(); return uid; } bool silenceInProducesSilenceOut() const override { return effect == nullptr || (effect->flags & effFlagsNoSoundInStop) != 0; } double getTailLengthSeconds() const override { if (effect == nullptr) return 0.0; const double sampleRate = getSampleRate(); if (sampleRate <= 0) return 0.0; VstIntPtr samples = dispatch (effGetTailSize, 0, 0, 0, 0); return samples / sampleRate; } bool acceptsMidi() const override { return wantsMidiMessages; } bool producesMidi() const override { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; } VstPlugCategory getVstCategory() const noexcept { return (VstPlugCategory) dispatch (effGetPlugCategory, 0, 0, 0, 0); } //============================================================================== void prepareToPlay (double rate, int samplesPerBlockExpected) override { setPlayConfigDetails (effect->numInputs, effect->numOutputs, rate, samplesPerBlockExpected); vstHostTime.tempo = 120.0; vstHostTime.timeSigNumerator = 4; vstHostTime.timeSigDenominator = 4; vstHostTime.sampleRate = rate; vstHostTime.samplePos = 0; vstHostTime.flags = kVstNanosValid | kVstAutomationWriting | kVstAutomationReading; initialise (rate, samplesPerBlockExpected); if (initialised) { wantsMidiMessages = wantsMidiMessages || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0); if (wantsMidiMessages) midiEventsToSend.ensureSize (256); else midiEventsToSend.freeEvents(); incomingMidi.clear(); dispatch (effSetSampleRate, 0, 0, 0, (float) rate); dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); if (! isPowerOn) setPower (true); // dodgy hack to force some plugins to initialise the sample rate.. if ((! hasEditor()) && getNumParameters() > 0) { const float old = getParameter (0); setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); setParameter (0, old); } dispatch (effStartProcess, 0, 0, 0, 0); setLatencySamples (effect->initialDelay); } } void releaseResources() override { if (initialised) { dispatch (effStopProcess, 0, 0, 0, 0); setPower (false); } tempBuffer.setSize (1, 1); incomingMidi.clear(); midiEventsToSend.freeEvents(); } void reset() override { if (isPowerOn) { setPower (false); setPower (true); } } void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override { const int numSamples = buffer.getNumSamples(); if (initialised) { if (AudioPlayHead* const playHead = getPlayHead()) { AudioPlayHead::CurrentPositionInfo position; if (playHead->getCurrentPosition (position)) { vstHostTime.samplePos = (double) position.timeInSamples; vstHostTime.tempo = position.bpm; vstHostTime.timeSigNumerator = position.timeSigNumerator; vstHostTime.timeSigDenominator = position.timeSigDenominator; vstHostTime.ppqPos = position.ppqPosition; vstHostTime.barStartPos = position.ppqPositionOfLastBarStart; vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid; VstInt32 newTransportFlags = 0; if (position.isPlaying) newTransportFlags |= kVstTransportPlaying; if (position.isRecording) newTransportFlags |= kVstTransportRecording; if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording))) vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged; else vstHostTime.flags &= ~kVstTransportChanged; switch (position.frameRate) { case AudioPlayHead::fps24: setHostTimeFrameRate (0, 24.0, position.timeInSeconds); break; case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break; case AudioPlayHead::fps2997: setHostTimeFrameRate (2, 29.97, position.timeInSeconds); break; case AudioPlayHead::fps30: setHostTimeFrameRate (3, 30.0, position.timeInSeconds); break; case AudioPlayHead::fps2997drop: setHostTimeFrameRate (4, 29.97, position.timeInSeconds); break; case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break; default: break; } if (position.isLooping) { vstHostTime.cycleStartPos = position.ppqLoopStart; vstHostTime.cycleEndPos = position.ppqLoopEnd; vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive); } else { vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive); } } } vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds(); if (wantsMidiMessages) { midiEventsToSend.clear(); midiEventsToSend.ensureSize (1); MidiBuffer::Iterator iter (midiMessages); const uint8* midiData; int numBytesOfMidiData, samplePosition; while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition)) { midiEventsToSend.addEvent (midiData, numBytesOfMidiData, jlimit (0, numSamples - 1, samplePosition)); } effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend.events, 0); } _clearfp(); if ((effect->flags & effFlagsCanReplacing) != 0) { effect->processReplacing (effect, buffer.getArrayOfWritePointers(), buffer.getArrayOfWritePointers(), numSamples); } else { tempBuffer.setSize (effect->numOutputs, numSamples); tempBuffer.clear(); effect->process (effect, buffer.getArrayOfWritePointers(), tempBuffer.getArrayOfWritePointers(), numSamples); for (int i = effect->numOutputs; --i >= 0;) buffer.copyFrom (i, 0, tempBuffer.getReadPointer (i), numSamples); } } else { // Not initialised, so just bypass.. for (int i = 0; i < getNumOutputChannels(); ++i) buffer.clear (i, 0, buffer.getNumSamples()); } { // copy any incoming midi.. const ScopedLock sl (midiInLock); midiMessages.swapWith (incomingMidi); incomingMidi.clear(); } } //============================================================================== bool hasEditor() const override { return effect != nullptr && (effect->flags & effFlagsHasEditor) != 0; } AudioProcessorEditor* createEditor() override; //============================================================================== const String getInputChannelName (int index) const override { if (index >= 0 && index < getNumInputChannels()) { VstPinProperties pinProps; if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) return String (pinProps.label, sizeof (pinProps.label)); } return String::empty; } bool isInputChannelStereoPair (int index) const override { if (index < 0 || index >= getNumInputChannels()) return false; VstPinProperties pinProps; if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) return (pinProps.flags & kVstPinIsStereo) != 0; return true; } const String getOutputChannelName (int index) const override { if (index >= 0 && index < getNumOutputChannels()) { VstPinProperties pinProps; if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) return String (pinProps.label, sizeof (pinProps.label)); } return String::empty; } bool isOutputChannelStereoPair (int index) const override { if (index < 0 || index >= getNumOutputChannels()) return false; VstPinProperties pinProps; if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) return (pinProps.flags & kVstPinIsStereo) != 0; return true; } bool isValidChannel (int index, bool isInput) const { return isPositiveAndBelow (index, isInput ? getNumInputChannels() : getNumOutputChannels()); } //============================================================================== int getNumParameters() { return effect != nullptr ? effect->numParams : 0; } float getParameter (int index) override { if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams)) { const ScopedLock sl (lock); return effect->getParameter (effect, index); } return 0.0f; } void setParameter (int index, float newValue) override { if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams)) { const ScopedLock sl (lock); if (effect->getParameter (effect, index) != newValue) effect->setParameter (effect, index, newValue); } } const String getParameterName (int index) override { return getTextForOpcode (index, effGetParamName); } const String getParameterText (int index) override { return getTextForOpcode (index, effGetParamDisplay); } String getParameterLabel (int index) const override { return getTextForOpcode (index, effGetParamLabel); } bool isParameterAutomatable (int index) const override { if (effect != nullptr) { jassert (index >= 0 && index < effect->numParams); return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0; } return false; } //============================================================================== int getNumPrograms() override { return effect != nullptr ? jmax (0, effect->numPrograms) : 0; } // NB: some plugs return negative numbers from this function. int getCurrentProgram() override { return (int) dispatch (effGetProgram, 0, 0, 0, 0); } void setCurrentProgram (int newIndex) override { if (getNumPrograms() > 0 && newIndex != getCurrentProgram()) dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0); } const String getProgramName (int index) override { if (index >= 0) { if (index == getCurrentProgram()) return getCurrentProgramName(); if (effect != nullptr) { char nm[264] = { 0 }; if (dispatch (effGetProgramNameIndexed, jlimit (0, getNumPrograms(), index), -1, nm, 0) != 0) return String::fromUTF8 (nm).trim(); } } return programNames [index]; } void changeProgramName (int index, const String& newName) override { if (index >= 0 && index == getCurrentProgram()) { if (getNumPrograms() > 0 && newName != getCurrentProgramName()) dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toRawUTF8(), 0.0f); } else { jassertfalse; // xxx not implemented! } } //============================================================================== void getStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, true); } void getCurrentProgramStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, false); } void setStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); } void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); } //============================================================================== void timerCallback() override { if (dispatch (effIdle, 0, 0, 0, 0) == 0) stopTimer(); } void handleAsyncUpdate() override { // indicates that something about the plugin has changed.. updateHostDisplay(); } VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) { switch (opcode) { case audioMasterAutomate: sendParamChangeMessageToListeners (index, opt); break; case audioMasterProcessEvents: handleMidiFromPlugin ((const VstEvents*) ptr); break; #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4311) #endif case audioMasterGetTime: return (VstIntPtr) &vstHostTime; #if JUCE_MSVC #pragma warning (pop) #endif case audioMasterIdle: if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread()) { const IdleCallRecursionPreventer icrp; #if JUCE_MAC if (getActiveEditor() != nullptr) dispatch (effEditIdle, 0, 0, 0, 0); #endif Timer::callPendingTimersSynchronously(); handleUpdateNowIfNeeded(); for (int i = ComponentPeer::getNumPeers(); --i >= 0;) if (ComponentPeer* p = ComponentPeer::getPeer(i)) p->performAnyPendingRepaintsNow(); } break; case audioMasterSizeWindow: if (AudioProcessorEditor* ed = getActiveEditor()) { #if JUCE_LINUX const MessageManagerLock mmLock; #endif ed->setSize (index, (int) value); } return 1; case audioMasterUpdateDisplay: triggerAsyncUpdate(); break; case audioMasterIOChanged: setLatencySamples (effect->initialDelay); break; case audioMasterNeedIdle: startTimer (50); break; case audioMasterGetSampleRate: return (VstIntPtr) (getSampleRate() > 0 ? getSampleRate() : defaultVSTSampleRateValue); case audioMasterGetBlockSize: return (VstIntPtr) (getBlockSize() > 0 ? getBlockSize() : defaultVSTBlockSizeValue); case audioMasterWantMidi: wantsMidiMessages = true; break; case audioMasterGetDirectory: return getVstDirectory(); case audioMasterTempoAt: if (extraFunctions != nullptr) return (VstIntPtr) extraFunctions->getTempoAt ((int64) value); break; case audioMasterGetAutomationState: if (extraFunctions != nullptr) return (VstIntPtr) extraFunctions->getAutomationState(); break; case audioMasterPinConnected: return isValidChannel (index, value == 0) ? 0 : 1; // (yes, 0 = true) case audioMasterGetCurrentProcessLevel: return isNonRealtime() ? 4 : 0; // none of these are handled (yet).. case audioMasterBeginEdit: case audioMasterEndEdit: case audioMasterSetTime: case audioMasterGetParameterQuantization: case audioMasterGetInputLatency: case audioMasterGetOutputLatency: case audioMasterGetPreviousPlug: case audioMasterGetNextPlug: case audioMasterWillReplaceOrAccumulate: case audioMasterOfflineStart: case audioMasterOfflineRead: case audioMasterOfflineWrite: case audioMasterOfflineGetCurrentPass: case audioMasterOfflineGetCurrentMetaPass: case audioMasterVendorSpecific: case audioMasterSetIcon: case audioMasterGetLanguage: case audioMasterOpenWindow: case audioMasterCloseWindow: break; default: return handleGeneralCallback (opcode, index, value, ptr, opt); } return 0; } // handles non plugin-specific callbacks.. static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 /*index*/, VstIntPtr /*value*/, void *ptr, float /*opt*/) { switch (opcode) { case audioMasterCanDo: { static const char* canDos[] = { "supplyIdle", "sendVstEvents", "sendVstMidiEvent", "sendVstTimeInfo", "receiveVstEvents", "receiveVstMidiEvent", "supportShell", "sizeWindow", "shellCategory" }; for (int i = 0; i < numElementsInArray (canDos); ++i) if (strcmp (canDos[i], (const char*) ptr) == 0) return 1; return 0; } case audioMasterVersion: return 2400; case audioMasterCurrentId: return shellUIDToCreate; case audioMasterGetNumAutomatableParameters: return 0; case audioMasterGetAutomationState: return 1; case audioMasterGetVendorVersion: return 0x0101; case audioMasterGetVendorString: case audioMasterGetProductString: { String hostName ("Juce VST Host"); if (JUCEApplicationBase* app = JUCEApplicationBase::getInstance()) hostName = app->getApplicationName(); hostName.copyToUTF8 ((char*) ptr, (size_t) jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); break; } case audioMasterGetSampleRate: return (VstIntPtr) defaultVSTSampleRateValue; case audioMasterGetBlockSize: return (VstIntPtr) defaultVSTBlockSizeValue; case audioMasterSetOutputSampleRate: return 0; default: DBG ("*** Unhandled VST Callback: " + String ((int) opcode)); break; } return 0; } //============================================================================== VstIntPtr dispatch (const int opcode, const int index, const VstIntPtr value, void* const ptr, float opt) const { VstIntPtr result = 0; if (effect != nullptr) { const ScopedLock sl (lock); const IdleCallRecursionPreventer icrp; try { #if JUCE_MAC const ResFileRefNum oldResFile = CurResFile(); if (module->resFileId != 0) UseResFile (module->resFileId); #endif result = effect->dispatcher (effect, opcode, index, value, ptr, opt); #if JUCE_MAC const ResFileRefNum newResFile = CurResFile(); if (newResFile != oldResFile) // avoid confusing the parent app's resource file with the plug-in's { module->resFileId = newResFile; UseResFile (oldResFile); } #endif } catch (...) {} } return result; } bool loadFromFXBFile (const void* const data, const size_t dataSize) { if (dataSize < 28) return false; const fxSet* const set = (const fxSet*) data; if ((! compareMagic (set->chunkMagic, "CcnK")) || fxbSwap (set->version) > fxbVersionNum) return false; if (compareMagic (set->fxMagic, "FxBk")) { // bank of programs if (fxbSwap (set->numPrograms) >= 0) { const int oldProg = getCurrentProgram(); const int numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams); const int progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float); for (int i = 0; i < fxbSwap (set->numPrograms); ++i) { if (i != oldProg) { const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen); if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize) return false; if (fxbSwap (set->numPrograms) > 0) setCurrentProgram (i); if (! restoreProgramSettings (prog)) return false; } } if (fxbSwap (set->numPrograms) > 0) setCurrentProgram (oldProg); const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize) return false; if (! restoreProgramSettings (prog)) return false; } } else if (compareMagic (set->fxMagic, "FxCk")) { // single program const fxProgram* const prog = (const fxProgram*) data; if (! compareMagic (prog->chunkMagic, "CcnK")) return false; changeProgramName (getCurrentProgram(), prog->prgName); for (int i = 0; i < fxbSwap (prog->numParams); ++i) setParameter (i, fxbSwapFloat (prog->params[i])); } else if (compareMagic (set->fxMagic, "FBCh")) { // non-preset chunk const fxChunkSet* const cset = (const fxChunkSet*) data; if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (size_t) dataSize) return false; setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false); } else if (compareMagic (set->fxMagic, "FPCh")) { // preset chunk const fxProgramSet* const cset = (const fxProgramSet*) data; if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (size_t) dataSize) return false; setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true); changeProgramName (getCurrentProgram(), cset->name); } else { return false; } return true; } bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB = 128) { const int numPrograms = getNumPrograms(); const int numParams = getNumParameters(); if (usesChunks()) { MemoryBlock chunk; getChunkData (chunk, ! isFXB, maxSizeMB); if (isFXB) { const size_t totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8; dest.setSize (totalLen, true); fxChunkSet* const set = (fxChunkSet*) dest.getData(); set->chunkMagic = fxbName ("CcnK"); set->byteSize = 0; set->fxMagic = fxbName ("FBCh"); set->version = fxbSwap (fxbVersionNum); set->fxID = fxbSwap (getUID()); set->fxVersion = fxbSwap (getVersionNumber()); set->numPrograms = fxbSwap (numPrograms); set->chunkSize = fxbSwap ((VstInt32) chunk.getSize()); chunk.copyTo (set->chunk, 0, chunk.getSize()); } else { const size_t totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8; dest.setSize (totalLen, true); fxProgramSet* const set = (fxProgramSet*) dest.getData(); set->chunkMagic = fxbName ("CcnK"); set->byteSize = 0; set->fxMagic = fxbName ("FPCh"); set->version = fxbSwap (fxbVersionNum); set->fxID = fxbSwap (getUID()); set->fxVersion = fxbSwap (getVersionNumber()); set->numPrograms = fxbSwap (numPrograms); set->chunkSize = fxbSwap ((VstInt32) chunk.getSize()); getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1); chunk.copyTo (set->chunk, 0, chunk.getSize()); } } else { if (isFXB) { const int progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float); const size_t len = (sizeof (fxSet) - sizeof (fxProgram)) + (size_t) (progLen * jmax (1, numPrograms)); dest.setSize (len, true); fxSet* const set = (fxSet*) dest.getData(); set->chunkMagic = fxbName ("CcnK"); set->byteSize = 0; set->fxMagic = fxbName ("FxBk"); set->version = fxbSwap (fxbVersionNum); set->fxID = fxbSwap (getUID()); set->fxVersion = fxbSwap (getVersionNumber()); set->numPrograms = fxbSwap (numPrograms); MemoryBlock oldSettings; createTempParameterStore (oldSettings); const int oldProgram = getCurrentProgram(); if (oldProgram >= 0) setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); for (int i = 0; i < numPrograms; ++i) { if (i != oldProgram) { setCurrentProgram (i); setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen)); } } if (oldProgram >= 0) setCurrentProgram (oldProgram); restoreFromTempParameterStore (oldSettings); } else { dest.setSize (sizeof (fxProgram) + (size_t) ((numParams - 1) * (int) sizeof (float)), true); setParamsInProgramBlock ((fxProgram*) dest.getData()); } } return true; } bool usesChunks() const noexcept { return effect != nullptr && (effect->flags & effFlagsProgramChunks) != 0; } bool getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const { if (usesChunks()) { void* data = nullptr; const size_t bytes = (size_t) dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); if (data != nullptr && bytes <= (size_t) maxSizeMB * 1024 * 1024) { mb.setSize (bytes); mb.copyFrom (data, 0, bytes); return true; } } return false; } bool setChunkData (const void* data, const int size, bool isPreset) { if (size > 0 && usesChunks()) { dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f); if (! isPreset) updateStoredProgramNames(); return true; } return false; } AEffect* effect; ModuleHandle::Ptr module; ScopedPointer extraFunctions; bool usesCocoaNSView; private: String name; CriticalSection lock; bool wantsMidiMessages, initialised, isPowerOn; mutable StringArray programNames; AudioSampleBuffer tempBuffer; CriticalSection midiInLock; MidiBuffer incomingMidi; VSTMidiEventList midiEventsToSend; VstTimeInfo vstHostTime; //============================================================================== void setHostTimeFrameRate (long frameRateIndex, double frameRate, double currentTime) noexcept { vstHostTime.flags |= kVstSmpteValid; vstHostTime.smpteFrameRate = (VstInt32) frameRateIndex; vstHostTime.smpteOffset = (VstInt32) (currentTime * 80.0 * frameRate + 0.5); } bool restoreProgramSettings (const fxProgram* const prog) { if (compareMagic (prog->chunkMagic, "CcnK") && compareMagic (prog->fxMagic, "FxCk")) { changeProgramName (getCurrentProgram(), prog->prgName); for (int i = 0; i < fxbSwap (prog->numParams); ++i) setParameter (i, fxbSwapFloat (prog->params[i])); return true; } return false; } String getTextForOpcode (const int index, const AEffectOpcodes opcode) const { if (effect == nullptr) return String::empty; jassert (index >= 0 && index < effect->numParams); char nm [256] = { 0 }; dispatch (opcode, index, 0, nm, 0); return String (CharPointer_UTF8 (nm)).trim(); } String getCurrentProgramName() { String progName; if (effect != nullptr) { { char nm[256] = { 0 }; dispatch (effGetProgramName, 0, 0, nm, 0); progName = String (CharPointer_UTF8 (nm)).trim(); } const int index = getCurrentProgram(); if (index >= 0 && programNames[index].isEmpty()) { while (programNames.size() < index) programNames.add (String::empty); programNames.set (index, progName); } } return progName; } void setParamsInProgramBlock (fxProgram* const prog) { const int numParams = getNumParameters(); prog->chunkMagic = fxbName ("CcnK"); prog->byteSize = 0; prog->fxMagic = fxbName ("FxCk"); prog->version = fxbSwap (fxbVersionNum); prog->fxID = fxbSwap (getUID()); prog->fxVersion = fxbSwap (getVersionNumber()); prog->numParams = fxbSwap (numParams); getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1); for (int i = 0; i < numParams; ++i) prog->params[i] = fxbSwapFloat (getParameter (i)); } void updateStoredProgramNames() { if (effect != nullptr && getNumPrograms() > 0) { char nm[256] = { 0 }; // only do this if the plugin can't use indexed names.. if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0) { const int oldProgram = getCurrentProgram(); MemoryBlock oldSettings; createTempParameterStore (oldSettings); for (int i = 0; i < getNumPrograms(); ++i) { setCurrentProgram (i); getCurrentProgramName(); // (this updates the list) } setCurrentProgram (oldProgram); restoreFromTempParameterStore (oldSettings); } } } void handleMidiFromPlugin (const VstEvents* const events) { if (events != nullptr) { const ScopedLock sl (midiInLock); VSTMidiEventList::addEventsToMidiBuffer (events, incomingMidi); } } //============================================================================== void createTempParameterStore (MemoryBlock& dest) { dest.setSize (64 + 4 * (size_t) getNumParameters()); dest.fillWith (0); getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); float* const p = (float*) (((char*) dest.getData()) + 64); for (int i = 0; i < getNumParameters(); ++i) p[i] = getParameter(i); } void restoreFromTempParameterStore (const MemoryBlock& m) { changeProgramName (getCurrentProgram(), (const char*) m.getData()); float* p = (float*) (((char*) m.getData()) + 64); for (int i = 0; i < getNumParameters(); ++i) setParameter (i, p[i]); } VstIntPtr getVstDirectory() const { #if JUCE_MAC return (VstIntPtr) (void*) &module->parentDirFSSpec; #else return (VstIntPtr) (pointer_sized_uint) module->fullParentDirectoryPathName.toRawUTF8(); #endif } //============================================================================== int getVersionNumber() const noexcept { return effect != nullptr ? effect->version : 0; } String getVersion() const { unsigned int v = (unsigned int) dispatch (effGetVendorVersion, 0, 0, 0, 0); String s; if (v == 0 || (int) v == -1) v = (unsigned int) getVersionNumber(); if (v != 0) { int versionBits[32]; int n = 0; for (unsigned int vv = v; vv != 0; vv /= 10) versionBits [n++] = vv % 10; if (n > 4) // if the number ends up silly, it's probably encoded as hex instead of decimal.. { n = 0; for (unsigned int vv = v; vv != 0; vv >>= 8) versionBits [n++] = vv & 255; } while (n > 1 && versionBits [n - 1] == 0) --n; s << 'V'; while (n > 0) { s << versionBits [--n]; if (n > 0) s << '.'; } } return s; } const char* getCategory() const { switch (getVstCategory()) { case kPlugCategEffect: return "Effect"; case kPlugCategSynth: return "Synth"; case kPlugCategAnalysis: return "Analysis"; case kPlugCategMastering: return "Mastering"; case kPlugCategSpacializer: return "Spacial"; case kPlugCategRoomFx: return "Reverb"; case kPlugSurroundFx: return "Surround"; case kPlugCategRestoration: return "Restoration"; case kPlugCategGenerator: return "Tone generation"; default: break; } return nullptr; } void setPower (const bool on) { dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0); isPowerOn = on; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance) }; //============================================================================== class VSTPluginWindow; static Array activeVSTWindows; //============================================================================== class VSTPluginWindow : public AudioProcessorEditor, #if ! JUCE_MAC public ComponentMovementWatcher, #endif public Timer { public: VSTPluginWindow (VSTPluginInstance& plug) : AudioProcessorEditor (&plug), #if ! JUCE_MAC ComponentMovementWatcher (this), #endif plugin (plug), isOpen (false), recursiveResize (false), pluginWantsKeys (false), pluginRefusesToResize (false), alreadyInside (false) { #if JUCE_WINDOWS pluginHWND = 0; sizeCheckCount = 0; #elif JUCE_LINUX pluginWindow = None; pluginProc = None; #elif JUCE_MAC #if JUCE_SUPPORT_CARBON if (! plug.usesCocoaNSView) addAndMakeVisible (carbonWrapper = new CarbonWrapperComponent (*this)); else #endif addAndMakeVisible (cocoaWrapper = new AutoResizingNSViewComponentWithParent()); #endif activeVSTWindows.add (this); setSize (1, 1); setOpaque (true); setVisible (true); } ~VSTPluginWindow() { closePluginWindow(); #if JUCE_MAC #if JUCE_SUPPORT_CARBON carbonWrapper = nullptr; #endif cocoaWrapper = nullptr; #endif activeVSTWindows.removeFirstMatchingValue (this); plugin.editorBeingDeleted (this); } //============================================================================== #if ! JUCE_MAC void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override { if (recursiveResize) return; Component* const topComp = getTopLevelComponent(); if (topComp->getPeer() != nullptr) { const Point pos (topComp->getLocalPoint (this, Point())); recursiveResize = true; #if JUCE_WINDOWS if (pluginHWND != 0) MoveWindow (pluginHWND, pos.getX(), pos.getY(), getWidth(), getHeight(), TRUE); #elif JUCE_LINUX if (pluginWindow != 0) { XMoveResizeWindow (display, pluginWindow, pos.getX(), pos.getY(), (unsigned int) getWidth(), (unsigned int) getHeight()); XMapRaised (display, pluginWindow); XFlush (display); } #endif recursiveResize = false; } } void componentVisibilityChanged() override { if (isShowing()) openPluginWindow(); else if (! shouldAvoidDeletingWindow()) closePluginWindow(); componentMovedOrResized (true, true); } void componentPeerChanged() override { closePluginWindow(); openPluginWindow(); } #endif #if JUCE_MAC void visibilityChanged() override { if (cocoaWrapper != nullptr) { if (isVisible()) openPluginWindow ((NSView*) cocoaWrapper->getView()); else closePluginWindow(); } } void childBoundsChanged (Component*) override { if (cocoaWrapper != nullptr) { int w = cocoaWrapper->getWidth(); int h = cocoaWrapper->getHeight(); if (w != getWidth() || h != getHeight()) setSize (w, h); } } #endif //============================================================================== bool keyStateChanged (bool) override { return pluginWantsKeys; } bool keyPressed (const juce::KeyPress&) override { return pluginWantsKeys; } //============================================================================== #if JUCE_MAC void paint (Graphics& g) override { g.fillAll (Colours::black); } #else void paint (Graphics& g) override { if (isOpen) { #if JUCE_LINUX if (pluginWindow != 0) { const Rectangle clip (g.getClipBounds()); XEvent ev = { 0 }; ev.xexpose.type = Expose; ev.xexpose.display = display; ev.xexpose.window = pluginWindow; ev.xexpose.x = clip.getX(); ev.xexpose.y = clip.getY(); ev.xexpose.width = clip.getWidth(); ev.xexpose.height = clip.getHeight(); sendEventToChild (ev); } #endif } else { g.fillAll (Colours::black); } } #endif //============================================================================== void timerCallback() override { if (isShowing()) { #if JUCE_WINDOWS if (--sizeCheckCount <= 0) { sizeCheckCount = 10; checkPluginWindowSize(); } #endif static bool reentrant = false; if (! reentrant) { reentrant = true; plugin.dispatch (effEditIdle, 0, 0, 0, 0); reentrant = false; } #if JUCE_LINUX if (pluginWindow == 0) { updatePluginWindowHandle(); if (pluginWindow != 0) componentMovedOrResized (true, true); } #endif } } //============================================================================== void mouseDown (const MouseEvent& e) override { (void) e; #if JUCE_LINUX if (pluginWindow == 0) return; toFront (true); XEvent ev; prepareXEvent (ev, e); ev.xbutton.type = ButtonPress; translateJuceToXButtonModifiers (e, ev); sendEventToChild (ev); #elif JUCE_WINDOWS toFront (true); #endif } void broughtToFront() override { activeVSTWindows.removeFirstMatchingValue (this); activeVSTWindows.add (this); #if JUCE_MAC dispatch (effEditTop, 0, 0, 0, 0); #endif } //============================================================================== private: VSTPluginInstance& plugin; bool isOpen, recursiveResize; bool pluginWantsKeys, pluginRefusesToResize, alreadyInside; #if JUCE_WINDOWS HWND pluginHWND; void* originalWndProc; int sizeCheckCount; #elif JUCE_LINUX Window pluginWindow; EventProcPtr pluginProc; #endif // This is a workaround for old Mackie plugins that crash if their // window is deleted more than once. bool shouldAvoidDeletingWindow() const { return plugin.getPluginDescription() .manufacturerName.containsIgnoreCase ("Loud Technologies"); } // This is an old workaround for some plugins that need a repaint when their // windows are first created, but it breaks some Izotope plugins.. bool shouldRepaintCarbonWindowWhenCreated() { return ! plugin.getName().containsIgnoreCase ("izotope"); } //============================================================================== #if JUCE_MAC void openPluginWindow (void* parentWindow) { if (isOpen || parentWindow == 0) return; isOpen = true; ERect* rect = nullptr; dispatch (effEditGetRect, 0, 0, &rect, 0); dispatch (effEditOpen, 0, 0, parentWindow, 0); // do this before and after like in the steinberg example dispatch (effEditGetRect, 0, 0, &rect, 0); dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code // Install keyboard hooks pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0); // double-check it's not too tiny int w = 250, h = 150; if (rect != nullptr) { w = rect->right - rect->left; h = rect->bottom - rect->top; if (w == 0 || h == 0) { w = 250; h = 150; } } w = jmax (w, 32); h = jmax (h, 32); setSize (w, h); startTimer (18 + juce::Random::getSystemRandom().nextInt (5)); repaint(); } #else void openPluginWindow() { if (isOpen || getWindowHandle() == 0) return; JUCE_VST_LOG ("Opening VST UI: " + plugin.getName()); isOpen = true; ERect* rect = nullptr; dispatch (effEditGetRect, 0, 0, &rect, 0); dispatch (effEditOpen, 0, 0, getWindowHandle(), 0); // do this before and after like in the steinberg example dispatch (effEditGetRect, 0, 0, &rect, 0); dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code // Install keyboard hooks pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0); #if JUCE_WINDOWS originalWndProc = 0; pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD); if (pluginHWND == 0) { isOpen = false; setSize (300, 150); return; } #pragma warning (push) #pragma warning (disable: 4244) if (! pluginWantsKeys) { originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC); SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc); } #pragma warning (pop) RECT r; GetWindowRect (pluginHWND, &r); int w = r.right - r.left; int h = r.bottom - r.top; if (rect != nullptr) { const int rw = rect->right - rect->left; const int rh = rect->bottom - rect->top; if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && rw != w && rh != h) || ((w == 0 && rw > 0) || (h == 0 && rh > 0))) { // very dodgy logic to decide which size is right. if (abs (rw - w) > 350 || abs (rh - h) > 350) { SetWindowPos (pluginHWND, 0, 0, 0, rw, rh, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); GetWindowRect (pluginHWND, &r); w = r.right - r.left; h = r.bottom - r.top; pluginRefusesToResize = (w != rw) || (h != rh); w = rw; h = rh; } } } #elif JUCE_LINUX updatePluginWindowHandle(); int w = 250, h = 150; if (rect != nullptr) { w = rect->right - rect->left; h = rect->bottom - rect->top; if (w == 0 || h == 0) { w = 250; h = 150; } } if (pluginWindow != 0) XMapRaised (display, pluginWindow); #endif // double-check it's not too tiny w = jmax (w, 32); h = jmax (h, 32); setSize (w, h); #if JUCE_WINDOWS checkPluginWindowSize(); #endif startTimer (18 + juce::Random::getSystemRandom().nextInt (5)); repaint(); } #endif //============================================================================== void closePluginWindow() { if (isOpen) { JUCE_VST_LOG ("Closing VST UI: " + plugin.getName()); isOpen = false; dispatch (effEditClose, 0, 0, 0, 0); stopTimer(); #if JUCE_WINDOWS #pragma warning (push) #pragma warning (disable: 4244) if (originalWndProc != 0 && pluginHWND != 0 && IsWindow (pluginHWND)) SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc); #pragma warning (pop) originalWndProc = 0; pluginHWND = 0; #elif JUCE_LINUX pluginWindow = 0; pluginProc = 0; #endif } } //============================================================================== VstIntPtr dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) { return plugin.dispatch (opcode, index, value, ptr, opt); } //============================================================================== #if JUCE_WINDOWS void checkPluginWindowSize() { RECT r; GetWindowRect (pluginHWND, &r); const int w = r.right - r.left; const int h = r.bottom - r.top; if (isShowing() && w > 0 && h > 0 && (w != getWidth() || h != getHeight()) && ! pluginRefusesToResize) { setSize (w, h); sizeCheckCount = 0; } } // hooks to get keyboard events from VST windows.. static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam) { for (int i = activeVSTWindows.size(); --i >= 0;) { Component::SafePointer w (activeVSTWindows[i]); if (w != nullptr && w->pluginHWND == hW) { if (message == WM_CHAR || message == WM_KEYDOWN || message == WM_SYSKEYDOWN || message == WM_KEYUP || message == WM_SYSKEYUP || message == WM_APPCOMMAND) { SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(), message, wParam, lParam); } if (w != nullptr) // (may have been deleted in SendMessage callback) return CallWindowProc ((WNDPROC) w->originalWndProc, (HWND) w->pluginHWND, message, wParam, lParam); } } return DefWindowProc (hW, message, wParam, lParam); } #endif #if JUCE_LINUX //============================================================================== // overload mouse/keyboard events to forward them to the plugin's inner window.. void sendEventToChild (XEvent& event) { if (pluginProc != 0) { // if the plugin publishes an event procedure, pass the event directly.. pluginProc (&event); } else if (pluginWindow != 0) { // if the plugin has a window, then send the event to the window so that // its message thread will pick it up.. XSendEvent (display, pluginWindow, False, NoEventMask, &event); XFlush (display); } } void prepareXEvent (XEvent& ev, const MouseEvent& e) const noexcept { zerostruct (ev); ev.xcrossing.display = display; ev.xcrossing.window = pluginWindow; ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); ev.xcrossing.time = CurrentTime; ev.xcrossing.x = e.x; ev.xcrossing.y = e.y; ev.xcrossing.x_root = e.getScreenX(); ev.xcrossing.y_root = e.getScreenY(); } void mouseEnter (const MouseEvent& e) override { if (pluginWindow != 0) { XEvent ev; prepareXEvent (ev, e); ev.xcrossing.type = EnterNotify; ev.xcrossing.mode = NotifyNormal; ev.xcrossing.detail = NotifyAncestor; translateJuceToXCrossingModifiers (e, ev); sendEventToChild (ev); } } void mouseExit (const MouseEvent& e) override { if (pluginWindow != 0) { XEvent ev; prepareXEvent (ev, e); ev.xcrossing.type = LeaveNotify; ev.xcrossing.mode = NotifyNormal; ev.xcrossing.detail = NotifyAncestor; ev.xcrossing.focus = hasKeyboardFocus (true); translateJuceToXCrossingModifiers (e, ev); sendEventToChild (ev); } } void mouseMove (const MouseEvent& e) override { if (pluginWindow != 0) { XEvent ev; prepareXEvent (ev, e); ev.xmotion.type = MotionNotify; ev.xmotion.is_hint = NotifyNormal; sendEventToChild (ev); } } void mouseDrag (const MouseEvent& e) override { if (pluginWindow != 0) { XEvent ev; prepareXEvent (ev, e); ev.xmotion.type = MotionNotify; ev.xmotion.is_hint = NotifyNormal; translateJuceToXMotionModifiers (e, ev); sendEventToChild (ev); } } void mouseUp (const MouseEvent& e) override { if (pluginWindow != 0) { XEvent ev; prepareXEvent (ev, e); ev.xbutton.type = ButtonRelease; translateJuceToXButtonModifiers (e, ev); sendEventToChild (ev); } } void mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel) override { if (pluginWindow != 0) { XEvent ev; prepareXEvent (ev, e); ev.xbutton.type = ButtonPress; translateJuceToXMouseWheelModifiers (e, wheel.deltaY, ev); sendEventToChild (ev); ev.xbutton.type = ButtonRelease; sendEventToChild (ev); } } void updatePluginWindowHandle() { pluginWindow = getChildWindow ((Window) getWindowHandle()); if (pluginWindow != 0) pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, XInternAtom (display, "_XEventProc", False)); } #endif //============================================================================== #if JUCE_MAC #if JUCE_SUPPORT_CARBON class CarbonWrapperComponent : public CarbonViewWrapperComponent { public: CarbonWrapperComponent (VSTPluginWindow& w) : owner (w), alreadyInside (false) { keepPluginWindowWhenHidden = w.shouldAvoidDeletingWindow(); setRepaintsChildHIViewWhenCreated (w.shouldRepaintCarbonWindowWhenCreated()); } ~CarbonWrapperComponent() { deleteWindow(); } HIViewRef attachView (WindowRef windowRef, HIViewRef /*rootView*/) override { owner.openPluginWindow (windowRef); return 0; } void removeView (HIViewRef) override { if (owner.isOpen) { owner.isOpen = false; owner.dispatch (effEditClose, 0, 0, 0, 0); owner.dispatch (effEditSleep, 0, 0, 0, 0); } } bool getEmbeddedViewSize (int& w, int& h) override { ERect* rect = nullptr; owner.dispatch (effEditGetRect, 0, 0, &rect, 0); w = rect->right - rect->left; h = rect->bottom - rect->top; return true; } void handleMouseDown (int x, int y) override { if (! alreadyInside) { alreadyInside = true; getTopLevelComponent()->toFront (true); owner.dispatch (effEditMouse, x, y, 0, 0); alreadyInside = false; } else { PostEvent (::mouseDown, 0); } } void handlePaint() override { if (ComponentPeer* const peer = getPeer()) { const Point pos (peer->globalToLocal (getScreenPosition())); ERect r; r.left = (VstInt16) pos.getX(); r.top = (VstInt16) pos.getY(); r.right = (VstInt16) (r.left + getWidth()); r.bottom = (VstInt16) (r.top + getHeight()); owner.dispatch (effEditDraw, 0, 0, &r, 0); } } private: VSTPluginWindow& owner; bool alreadyInside; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CarbonWrapperComponent) }; friend class CarbonWrapperComponent; ScopedPointer carbonWrapper; #endif ScopedPointer cocoaWrapper; void resized() override { #if JUCE_SUPPORT_CARBON if (carbonWrapper != nullptr) carbonWrapper->setSize (getWidth(), getHeight()); #endif if (cocoaWrapper != nullptr) cocoaWrapper->setSize (getWidth(), getHeight()); } #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginWindow) }; //============================================================================== AudioProcessorEditor* VSTPluginInstance::createEditor() { return hasEditor() ? new VSTPluginWindow (*this) : nullptr; } //============================================================================== // entry point for all callbacks from the plugin static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) { if (effect != nullptr) if (VSTPluginInstance* instance = (VSTPluginInstance*) (effect->resvd2)) return instance->handleCallback (opcode, index, value, ptr, opt); return VSTPluginInstance::handleGeneralCallback (opcode, index, value, ptr, opt); } //============================================================================== VSTPluginFormat::VSTPluginFormat() {} VSTPluginFormat::~VSTPluginFormat() {} static VSTPluginInstance* createAndUpdateDesc (VSTPluginFormat& format, PluginDescription& desc) { if (AudioPluginInstance* p = format.createInstanceFromDescription (desc, 44100.0, 512)) { if (VSTPluginInstance* instance = dynamic_cast (p)) { #if JUCE_MAC if (instance->module->resFileId != 0) UseResFile (instance->module->resFileId); #endif instance->fillInPluginDescription (desc); return instance; } jassertfalse; } return nullptr; } void VSTPluginFormat::findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) { if (! fileMightContainThisPluginType (fileOrIdentifier)) return; PluginDescription desc; desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; ScopedPointer instance (createAndUpdateDesc (*this, desc)); if (instance == nullptr) return; if (instance->getVstCategory() != kPlugCategShell) { // Normal plugin... results.add (new PluginDescription (desc)); instance->dispatch (effOpen, 0, 0, 0, 0); } else { // It's a shell plugin, so iterate all the subtypes... for (;;) { char shellEffectName [256] = { 0 }; const int uid = (int) instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0); if (uid == 0) break; desc.uid = uid; desc.name = shellEffectName; aboutToScanVSTShellPlugin (desc); ScopedPointer shellInstance (createAndUpdateDesc (*this, desc)); if (shellInstance != nullptr) { jassert (desc.uid == uid); desc.hasSharedContainer = true; desc.name = shellEffectName; if (! arrayContainsPlugin (results, desc)) results.add (new PluginDescription (desc)); } } } } AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc, double sampleRate, int blockSize) { ScopedPointer result; if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { File file (desc.fileOrIdentifier); const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); file.getParentDirectory().setAsCurrentWorkingDirectory(); if (ModuleHandle::Ptr module = ModuleHandle::findOrCreateModule (file)) { shellUIDToCreate = desc.uid; result = new VSTPluginInstance (module); if (result->effect != nullptr) { result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) (VSTPluginInstance*) result; result->initialise (sampleRate, blockSize); } else { result = nullptr; } } previousWorkingDirectory.setAsCurrentWorkingDirectory(); } return result.release(); } bool VSTPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); #if JUCE_MAC if (f.isDirectory() && f.hasFileExtension (".vst")) return true; #if JUCE_PPC FSRef fileRef; if (makeFSRefFromPath (&fileRef, f.getFullPathName())) { const short resFileId = FSOpenResFile (&fileRef, fsRdPerm); if (resFileId != -1) { const int numEffects = Count1Resources ('aEff'); CloseResFile (resFileId); if (numEffects > 0) return true; } } #endif return false; #elif JUCE_WINDOWS return f.existsAsFile() && f.hasFileExtension (".dll"); #elif JUCE_LINUX return f.existsAsFile() && f.hasFileExtension (".so"); #endif } String VSTPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } bool VSTPluginFormat::pluginNeedsRescanning (const PluginDescription& desc) { return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime; } bool VSTPluginFormat::doesPluginStillExist (const PluginDescription& desc) { return File (desc.fileOrIdentifier).exists(); } StringArray VSTPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive) { StringArray results; for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) recursiveFileSearch (results, directoriesToSearch [j], recursive); return results; } void VSTPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive) { // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside // .component or .vst directories. DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); while (iter.next()) { const File f (iter.getFile()); bool isPlugin = false; if (fileMightContainThisPluginType (f.getFullPathName())) { isPlugin = true; results.add (f.getFullPathName()); } if (recursive && (! isPlugin) && f.isDirectory()) recursiveFileSearch (results, f, true); } } FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() { #if JUCE_MAC return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST"); #elif JUCE_LINUX return FileSearchPath (SystemStats::getEnvironmentVariable ("VST_PATH", "/usr/lib/vst;/usr/local/lib/vst;~/.vst") .replace (":", ";")); #elif JUCE_WINDOWS const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); FileSearchPath paths; paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", programFiles + "\\Steinberg\\VstPlugins")); paths.removeNonExistentPaths(); paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", programFiles + "\\VstPlugins")); return paths; #endif } const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin) { if (VSTPluginInstance* const vst = dynamic_cast (plugin)) if (vst->module != nullptr) return vst->module->vstXml.get(); return nullptr; } bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize) { if (VSTPluginInstance* vst = dynamic_cast (plugin)) return vst->loadFromFXBFile (data, dataSize); return false; } bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& dest, bool asFXB) { if (VSTPluginInstance* vst = dynamic_cast (plugin)) return vst->saveToFXBFile (dest, asFXB); return false; } bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset) { if (VSTPluginInstance* vst = dynamic_cast (plugin)) return vst->getChunkData (result, isPreset, 128); return false; } bool VSTPluginFormat::setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset) { if (VSTPluginInstance* vst = dynamic_cast (plugin)) return vst->setChunkData (data, size, isPreset); return false; } void VSTPluginFormat::setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions) { ScopedPointer f (functions); if (VSTPluginInstance* vst = dynamic_cast (plugin)) vst->extraFunctions = f; } VSTPluginFormat::VstIntPtr JUCE_CALLTYPE VSTPluginFormat::dispatcher (AudioPluginInstance* plugin, int32 opcode, int32 index, VstIntPtr value, void* ptr, float opt) { if (VSTPluginInstance* vst = dynamic_cast (plugin)) return vst->dispatch (opcode, index, value, ptr, opt); return 0; } void VSTPluginFormat::aboutToScanVSTShellPlugin (const PluginDescription&) {} #endif juce_VSTPluginFormat.h000066400000000000000000000116341320201440200350220ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/format_types/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_PLUGINHOST_VST || DOXYGEN //============================================================================== /** Implements a plugin format manager for VSTs. */ class JUCE_API VSTPluginFormat : public AudioPluginFormat { public: //============================================================================== VSTPluginFormat(); ~VSTPluginFormat(); //============================================================================== /** Attempts to retreive the VSTXML data from a plugin. Will return nullptr if the plugin isn't a VST, or if it doesn't have any VSTXML. */ static const XmlElement* getVSTXML (AudioPluginInstance* plugin); /** Attempts to reload a VST plugin's state from some FXB or FXP data. */ static bool loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize); /** Attempts to save a VST's state to some FXP or FXB data. */ static bool saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& result, bool asFXB); /** Attempts to get a VST's state as a chunk of memory. */ static bool getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset); /** Attempts to set a VST's state from a chunk of memory. */ static bool setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset); //============================================================================== /** Base class for some extra functions that can be attached to a VST plugin instance. */ class ExtraFunctions { public: virtual ~ExtraFunctions() {} /** This should return 10000 * the BPM at this position in the current edit. */ virtual int64 getTempoAt (int64 samplePos) = 0; /** This should return the host's automation state. @returns 0 = not supported, 1 = off, 2 = read, 3 = write, 4 = read/write */ virtual int getAutomationState() = 0; }; /** Provides an ExtraFunctions callback object for a plugin to use. The plugin will take ownership of the object and will delete it automatically. */ static void setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions); //============================================================================== #if JUCE_64BIT typedef int64 VstIntPtr; #else typedef int32 VstIntPtr; #endif /** This simply calls directly to the VST's AEffect::dispatcher() function. */ static VstIntPtr JUCE_CALLTYPE dispatcher (AudioPluginInstance*, int32, int32, VstIntPtr, void*, float); //============================================================================== String getName() const override { return "VST"; } void findAllTypesForFile (OwnedArray&, const String& fileOrIdentifier) override; AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override; bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; bool pluginNeedsRescanning (const PluginDescription&) override; StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override; bool doesPluginStillExist (const PluginDescription&) override; FileSearchPath getDefaultLocationsToSearch() override; bool canScanForPlugins() const override { return true; } /** Can be overridden to receive a callback when each member of a shell plugin is about to be tested during a call to findAllTypesForFile(). Only the name and uid members of the PluginDescription are guaranteed to be valid when this is called. */ virtual void aboutToScanVSTShellPlugin (const PluginDescription&); private: void recursiveFileSearch (StringArray&, const File&, bool recursive); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginFormat) }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp000066400000000000000000000121611320201440200332730ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if defined (JUCE_AUDIO_PROCESSORS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_audio_processors.h" #include "../juce_gui_extra/juce_gui_extra.h" //============================================================================== #if JUCE_MAC #if JUCE_SUPPORT_CARBON \ && ((JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \ || ! (defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)) #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #define Component CarbonDummyCompName #include #undef Point #undef Component #endif #endif #if JUCE_PLUGINHOST_VST && JUCE_LINUX #include #include #undef KeyPress #endif #if ! JUCE_WINDOWS && ! JUCE_MAC #undef JUCE_PLUGINHOST_VST3 #define JUCE_PLUGINHOST_VST3 0 #endif //============================================================================== namespace juce { static inline bool arrayContainsPlugin (const OwnedArray& list, const PluginDescription& desc) { for (int i = list.size(); --i >= 0;) if (list.getUnchecked(i)->isDuplicateOf (desc)) return true; return false; } #if JUCE_MAC //============================================================================== struct AutoResizingNSViewComponent : public NSViewComponent, private AsyncUpdater { AutoResizingNSViewComponent() : recursive (false) {} void childBoundsChanged (Component*) override { if (recursive) { triggerAsyncUpdate(); } else { recursive = true; resizeToFitView(); recursive = true; } } void handleAsyncUpdate() override { resizeToFitView(); } bool recursive; }; //============================================================================== struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent, private Timer { AutoResizingNSViewComponentWithParent() { NSView* v = [[NSView alloc] init]; setView (v); [v release]; startTimer (30); } NSView* getChildView() const { if (NSView* parent = (NSView*) getView()) if ([[parent subviews] count] > 0) return [[parent subviews] objectAtIndex: 0]; return nil; } void timerCallback() override { if (NSView* child = getChildView()) { stopTimer(); setView (child); } } }; #endif #if JUCE_CLANG #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif #include "format/juce_AudioPluginFormat.cpp" #include "format/juce_AudioPluginFormatManager.cpp" #include "processors/juce_AudioProcessor.cpp" #include "processors/juce_AudioProcessorEditor.cpp" #include "processors/juce_AudioProcessorGraph.cpp" #include "processors/juce_GenericAudioProcessorEditor.cpp" #include "processors/juce_PluginDescription.cpp" #include "format_types/juce_LADSPAPluginFormat.cpp" #include "format_types/juce_VSTPluginFormat.cpp" #include "format_types/juce_VST3PluginFormat.cpp" #include "format_types/juce_AudioUnitPluginFormat.mm" #include "scanning/juce_KnownPluginList.cpp" #include "scanning/juce_PluginDirectoryScanner.cpp" #include "scanning/juce_PluginListComponent.cpp" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h000066400000000000000000000073011320201440200327400ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIO_PROCESSORS_H_INCLUDED #define JUCE_AUDIO_PROCESSORS_H_INCLUDED #include "../juce_gui_basics/juce_gui_basics.h" #include "../juce_audio_basics/juce_audio_basics.h" //============================================================================= /** Config: JUCE_PLUGINHOST_VST Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be installed on your machine. @see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3 */ #ifndef JUCE_PLUGINHOST_VST #define JUCE_PLUGINHOST_VST 0 #endif /** Config: JUCE_PLUGINHOST_VST3 Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be installed on your machine. @see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU */ #ifndef JUCE_PLUGINHOST_VST3 #define JUCE_PLUGINHOST_VST3 0 #endif /** Config: JUCE_PLUGINHOST_AU Enables the AudioUnit plugin hosting classes. This is Mac-only, of course. @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_VST3 */ #ifndef JUCE_PLUGINHOST_AU #define JUCE_PLUGINHOST_AU 0 #endif #if ! (JUCE_PLUGINHOST_AU || JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3) // #error "You need to set either the JUCE_PLUGINHOST_AU and/or JUCE_PLUGINHOST_VST and/or JUCE_PLUGINHOST_VST3 flags if you're using this module!" #endif #if ! (defined (JUCE_SUPPORT_CARBON) || JUCE_64BIT) #define JUCE_SUPPORT_CARBON 1 #endif //============================================================================= //============================================================================= namespace juce { class AudioProcessor; #include "processors/juce_AudioPlayHead.h" #include "processors/juce_AudioProcessorEditor.h" #include "processors/juce_AudioProcessorListener.h" #include "processors/juce_AudioProcessorParameter.h" #include "processors/juce_AudioProcessor.h" #include "processors/juce_PluginDescription.h" #include "processors/juce_AudioPluginInstance.h" #include "processors/juce_AudioProcessorGraph.h" #include "processors/juce_GenericAudioProcessorEditor.h" #include "format/juce_AudioPluginFormat.h" #include "format/juce_AudioPluginFormatManager.h" #include "scanning/juce_KnownPluginList.h" #include "format_types/juce_AudioUnitPluginFormat.h" #include "format_types/juce_LADSPAPluginFormat.h" #include "format_types/juce_VSTMidiEventList.h" #include "format_types/juce_VSTPluginFormat.h" #include "format_types/juce_VST3PluginFormat.h" #include "scanning/juce_PluginDirectoryScanner.h" #include "scanning/juce_PluginListComponent.h" } #endif // JUCE_AUDIO_PROCESSORS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.mm000066400000000000000000000017111320201440200331210ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_audio_processors.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/juce_module_info000066400000000000000000000017361320201440200314350ustar00rootroot00000000000000{ "id": "juce_audio_processors", "name": "JUCE audio plugin hosting classes", "version": "3.2.0", "description": "Classes for loading and playing VST, AU, or internally-generated audio processors.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", "dependencies": [ { "id": "juce_gui_extra", "version": "matching" }, { "id": "juce_audio_basics", "version": "matching" } ], "include": "juce_audio_processors.h", "compile": [ { "file": "juce_audio_processors.cpp", "target": "! xcode" }, { "file": "juce_audio_processors.mm", "target": "xcode" } ], "browse": [ "processors/*", "format/*", "format_types/*", "scanning/*" ], "OSXFrameworks": "CoreAudio CoreMIDI AudioToolbox", "iOSFrameworks": "AudioToolbox" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/000077500000000000000000000000001320201440200303775ustar00rootroot00000000000000juce_AudioPlayHead.h000066400000000000000000000124051320201440200341520ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPLAYHEAD_H_INCLUDED #define JUCE_AUDIOPLAYHEAD_H_INCLUDED //============================================================================== /** A subclass of AudioPlayHead can supply information about the position and status of a moving play head during audio playback. One of these can be supplied to an AudioProcessor object so that it can find out about the position of the audio that it is rendering. @see AudioProcessor::setPlayHead, AudioProcessor::getPlayHead */ class JUCE_API AudioPlayHead { protected: //============================================================================== AudioPlayHead() {} public: virtual ~AudioPlayHead() {} //============================================================================== /** Frame rate types. */ enum FrameRateType { fps24 = 0, fps25 = 1, fps2997 = 2, fps30 = 3, fps2997drop = 4, fps30drop = 5, fpsUnknown = 99 }; //============================================================================== /** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method. */ struct JUCE_API CurrentPositionInfo { /** The tempo in BPM */ double bpm; /** Time signature numerator, e.g. the 3 of a 3/4 time sig */ int timeSigNumerator; /** 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. */ int64 timeInSamples; /** The current play position, in seconds from the start of the edit. */ double timeInSeconds; /** For timecode, the position of the start of the edit, in seconds from 00:00:00:00. */ double editOriginTime; /** The current play position, in pulses-per-quarter-note. */ double ppqPosition; /** 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 bar, in ppq units. Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If it's not available, the value will be 0. */ double ppqPositionOfLastBarStart; /** The video frame rate, if applicable. */ FrameRateType frameRate; /** True if the transport is currently playing. */ bool isPlaying; /** True if the transport is currently recording. (When isRecording is true, then isPlaying will also be true). */ bool isRecording; /** The current cycle start position in pulses-per-quarter-note. Note that not all hosts or plugin formats may provide this value. @see isLooping */ double ppqLoopStart; /** The current cycle end position in pulses-per-quarter-note. Note that not all hosts or plugin formats may provide this value. @see isLooping */ double ppqLoopEnd; /** True if the transport is currently looping. */ bool isLooping; //============================================================================== bool operator== (const CurrentPositionInfo& other) const noexcept; bool operator!= (const CurrentPositionInfo& other) const noexcept; void resetToDefault(); }; //============================================================================== /** Fills-in the given structure with details about the transport's position at the start of the current processing block. If this method returns false then the current play head position is not available and the given structure will be undefined. You can ONLY call this from your processBlock() method! Calling it at other times will produce undefined behaviour, as the host may not have any context in which a time would make sense, and some hosts will almost certainly have multithreading issues if it's not called on the audio thread. */ virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0; }; #endif // JUCE_AUDIOPLAYHEAD_H_INCLUDED juce_AudioPluginInstance.h000066400000000000000000000062641320201440200354140ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPLUGININSTANCE_H_INCLUDED #define JUCE_AUDIOPLUGININSTANCE_H_INCLUDED //============================================================================== /** Base class for an active instance of a plugin. This derives from the AudioProcessor class, and adds some extra functionality that helps when wrapping dynamically loaded plugins. This class is not needed when writing plugins, and you should never need to derive your own sub-classes from it. The plugin hosting classes use it internally and will return AudioPluginInstance objects which wrap external plugins. @see AudioProcessor, AudioPluginFormat */ class JUCE_API AudioPluginInstance : public AudioProcessor { public: //============================================================================== /** Destructor. Make sure that you delete any UI components that belong to this plugin before deleting the plugin. */ virtual ~AudioPluginInstance() {} //============================================================================== /** Fills-in the appropriate parts of this plugin description object. */ virtual void fillInPluginDescription (PluginDescription& description) const = 0; /** Returns a PluginDescription for this plugin. This is just a convenience method to avoid calling fillInPluginDescription. */ PluginDescription getPluginDescription() const { PluginDescription desc; fillInPluginDescription (desc); return desc; } /** Returns a pointer to some kind of platform-specific data about the plugin. E.g. For a VST, this value can be cast to an AEffect*. For an AudioUnit, it can be cast to an AudioUnit handle. */ virtual void* getPlatformSpecificData() { return nullptr; } /** For some formats (currently AudioUnit), this forces a reload of the list of available parameters. */ virtual void refreshParameterList() {} protected: //============================================================================== AudioPluginInstance() {} JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance) }; #endif // JUCE_AUDIOPLUGININSTANCE_H_INCLUDED juce_AudioProcessor.cpp000066400000000000000000000362251320201440200350030ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ static ThreadLocalValue wrapperTypeBeingCreated; void JUCE_CALLTYPE AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::WrapperType type) { wrapperTypeBeingCreated = type; } AudioProcessor::AudioProcessor() : wrapperType (wrapperTypeBeingCreated.get()), playHead (nullptr), sampleRate (0), blockSize (0), numInputChannels (0), numOutputChannels (0), latencySamples (0), suspended (false), nonRealtime (false) { } AudioProcessor::~AudioProcessor() { // ooh, nasty - the editor should have been deleted before the filter // that it refers to is deleted.. jassert (activeEditor == nullptr); #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING // This will fail if you've called beginParameterChangeGesture() for one // or more parameters without having made a corresponding call to endParameterChangeGesture... jassert (changingParams.countNumberOfSetBits() == 0); #endif } void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead) { playHead = newPlayHead; } void AudioProcessor::addListener (AudioProcessorListener* const newListener) { const ScopedLock sl (listenerLock); listeners.addIfNotAlreadyThere (newListener); } void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRemove) { const ScopedLock sl (listenerLock); listeners.removeFirstMatchingValue (listenerToRemove); } void AudioProcessor::setPlayConfigDetails (const int newNumIns, const int newNumOuts, const double newSampleRate, const int newBlockSize) noexcept { sampleRate = newSampleRate; blockSize = newBlockSize; if (numInputChannels != newNumIns || numOutputChannels != newNumOuts) { numInputChannels = newNumIns; numOutputChannels = newNumOuts; numChannelsChanged(); } } void AudioProcessor::numChannelsChanged() {} void AudioProcessor::setSpeakerArrangement (const String& inputs, const String& outputs) { inputSpeakerArrangement = inputs; outputSpeakerArrangement = outputs; } void AudioProcessor::setNonRealtime (const bool newNonRealtime) noexcept { nonRealtime = newNonRealtime; } void AudioProcessor::setLatencySamples (const int newLatency) { if (latencySamples != newLatency) { latencySamples = newLatency; updateHostDisplay(); } } void AudioProcessor::setParameterNotifyingHost (const int parameterIndex, const float newValue) { setParameter (parameterIndex, newValue); sendParamChangeMessageToListeners (parameterIndex, newValue); } AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) const noexcept { const ScopedLock sl (listenerLock); return listeners [index]; } void AudioProcessor::sendParamChangeMessageToListeners (const int parameterIndex, const float newValue) { if (isPositiveAndBelow (parameterIndex, getNumParameters())) { for (int i = listeners.size(); --i >= 0;) if (AudioProcessorListener* l = getListenerLocked (i)) l->audioProcessorParameterChanged (this, parameterIndex, newValue); } else { jassertfalse; // called with an out-of-range parameter index! } } void AudioProcessor::beginParameterChangeGesture (int parameterIndex) { if (isPositiveAndBelow (parameterIndex, getNumParameters())) { #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING // This means you've called beginParameterChangeGesture twice in succession without a matching // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. jassert (! changingParams [parameterIndex]); changingParams.setBit (parameterIndex); #endif for (int i = listeners.size(); --i >= 0;) if (AudioProcessorListener* l = getListenerLocked (i)) l->audioProcessorParameterChangeGestureBegin (this, parameterIndex); } else { jassertfalse; // called with an out-of-range parameter index! } } void AudioProcessor::endParameterChangeGesture (int parameterIndex) { if (isPositiveAndBelow (parameterIndex, getNumParameters())) { #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING // This means you've called endParameterChangeGesture without having previously called // beginParameterChangeGesture. That might be fine in most hosts, but better to keep the // calls matched correctly. jassert (changingParams [parameterIndex]); changingParams.clearBit (parameterIndex); #endif for (int i = listeners.size(); --i >= 0;) if (AudioProcessorListener* l = getListenerLocked (i)) l->audioProcessorParameterChangeGestureEnd (this, parameterIndex); } else { jassertfalse; // called with an out-of-range parameter index! } } void AudioProcessor::updateHostDisplay() { for (int i = listeners.size(); --i >= 0;) if (AudioProcessorListener* l = getListenerLocked (i)) l->audioProcessorChanged (this); } const OwnedArray& AudioProcessor::getParameters() const noexcept { return managedParameters; } int AudioProcessor::getNumParameters() { return managedParameters.size(); } float AudioProcessor::getParameter (int index) { if (AudioProcessorParameter* p = getParamChecked (index)) return p->getValue(); return 0; } void AudioProcessor::setParameter (int index, float newValue) { if (AudioProcessorParameter* p = getParamChecked (index)) p->setValue (newValue); } float AudioProcessor::getParameterDefaultValue (int index) { if (AudioProcessorParameter* p = managedParameters[index]) return p->getDefaultValue(); return 0; } const String AudioProcessor::getParameterName (int index) { if (AudioProcessorParameter* p = getParamChecked (index)) return p->getName (512); return String(); } String AudioProcessor::getParameterName (int index, int maximumStringLength) { if (AudioProcessorParameter* p = managedParameters[index]) return p->getName (maximumStringLength); return getParameterName (index).substring (0, maximumStringLength); } const String AudioProcessor::getParameterText (int index) { return getParameterText (index, 1024); } String AudioProcessor::getParameterText (int index, int maximumStringLength) { if (AudioProcessorParameter* p = managedParameters[index]) return p->getText (p->getValue(), maximumStringLength); return getParameterText (index).substring (0, maximumStringLength); } int AudioProcessor::getParameterNumSteps (int index) { if (AudioProcessorParameter* p = managedParameters[index]) return p->getNumSteps(); return AudioProcessor::getDefaultNumParameterSteps(); } int AudioProcessor::getDefaultNumParameterSteps() noexcept { return 0x7fffffff; } String AudioProcessor::getParameterLabel (int index) const { if (AudioProcessorParameter* p = managedParameters[index]) return p->getLabel(); return String(); } bool AudioProcessor::isParameterAutomatable (int index) const { if (AudioProcessorParameter* p = managedParameters[index]) return p->isAutomatable(); return true; } bool AudioProcessor::isParameterOrientationInverted (int index) const { if (AudioProcessorParameter* p = managedParameters[index]) return p->isOrientationInverted(); return false; } bool AudioProcessor::isMetaParameter (int index) const { if (AudioProcessorParameter* p = managedParameters[index]) return p->isMetaParameter(); return false; } AudioProcessorParameter* AudioProcessor::getParamChecked (int index) const noexcept { AudioProcessorParameter* p = managedParameters[index]; // If you hit this, then you're either trying to access parameters that are out-of-range, // or you're not using addParameter and the managed parameter list, but have failed // to override some essential virtual methods and implement them appropriately. jassert (p != nullptr); return p; } void AudioProcessor::addParameter (AudioProcessorParameter* p) { p->processor = this; p->parameterIndex = managedParameters.size(); managedParameters.add (p); } void AudioProcessor::suspendProcessing (const bool shouldBeSuspended) { const ScopedLock sl (callbackLock); suspended = shouldBeSuspended; } void AudioProcessor::reset() {} void AudioProcessor::processBlockBypassed (AudioSampleBuffer&, MidiBuffer&) {} //============================================================================== void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noexcept { const ScopedLock sl (callbackLock); if (activeEditor == editor) activeEditor = nullptr; } AudioProcessorEditor* AudioProcessor::createEditorIfNeeded() { if (activeEditor != nullptr) return activeEditor; AudioProcessorEditor* const ed = createEditor(); if (ed != nullptr) { // you must give your editor comp a size before returning it.. jassert (ed->getWidth() > 0 && ed->getHeight() > 0); const ScopedLock sl (callbackLock); activeEditor = ed; } // You must make your hasEditor() method return a consistent result! jassert (hasEditor() == (ed != nullptr)); return ed; } //============================================================================== void AudioProcessor::getCurrentProgramStateInformation (juce::MemoryBlock& destData) { getStateInformation (destData); } void AudioProcessor::setCurrentProgramStateInformation (const void* data, int sizeInBytes) { setStateInformation (data, sizeInBytes); } //============================================================================== // magic number to identify memory blocks that we've stored as XML const uint32 magicXmlNumber = 0x21324356; void AudioProcessor::copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& destData) { { MemoryOutputStream out (destData, false); out.writeInt (magicXmlNumber); out.writeInt (0); xml.writeToStream (out, String(), true, false); out.writeByte (0); } // go back and write the string length.. static_cast (destData.getData())[1] = ByteOrder::swapIfBigEndian ((uint32) destData.getSize() - 9); } XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeInBytes) { if (sizeInBytes > 8 && ByteOrder::littleEndianInt (data) == magicXmlNumber) { const int stringLength = (int) ByteOrder::littleEndianInt (addBytesToPointer (data, 4)); if (stringLength > 0) return XmlDocument::parse (String::fromUTF8 (static_cast (data) + 8, jmin ((sizeInBytes - 8), stringLength))); } return nullptr; } //============================================================================== void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {} void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} //============================================================================== AudioProcessorParameter::AudioProcessorParameter() noexcept : processor (nullptr), parameterIndex (-1) {} AudioProcessorParameter::~AudioProcessorParameter() {} void AudioProcessorParameter::setValueNotifyingHost (float newValue) { // This method can't be used until the parameter has been attached to a processor! jassert (processor != nullptr && parameterIndex >= 0); return processor->setParameterNotifyingHost (parameterIndex, newValue); } void AudioProcessorParameter::beginChangeGesture() { // This method can't be used until the parameter has been attached to a processor! jassert (processor != nullptr && parameterIndex >= 0); processor->beginParameterChangeGesture (parameterIndex); } void AudioProcessorParameter::endChangeGesture() { // This method can't be used until the parameter has been attached to a processor! jassert (processor != nullptr && parameterIndex >= 0); processor->endParameterChangeGesture (parameterIndex); } bool AudioProcessorParameter::isOrientationInverted() const { return false; } bool AudioProcessorParameter::isAutomatable() const { return true; } bool AudioProcessorParameter::isMetaParameter() const { return false; } int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); } String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const { return String (value, 2); } //============================================================================== bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept { return timeInSamples == other.timeInSamples && ppqPosition == other.ppqPosition && editOriginTime == other.editOriginTime && ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart && frameRate == other.frameRate && isPlaying == other.isPlaying && isRecording == other.isRecording && bpm == other.bpm && timeSigNumerator == other.timeSigNumerator && timeSigDenominator == other.timeSigDenominator && ppqLoopStart == other.ppqLoopStart && ppqLoopEnd == other.ppqLoopEnd && isLooping == other.isLooping; } bool AudioPlayHead::CurrentPositionInfo::operator!= (const CurrentPositionInfo& other) const noexcept { return ! operator== (other); } void AudioPlayHead::CurrentPositionInfo::resetToDefault() { zerostruct (*this); timeSigNumerator = 4; timeSigDenominator = 4; bpm = 120; } juce_AudioProcessor.h000066400000000000000000001051321320201440200344420ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPROCESSOR_H_INCLUDED #define JUCE_AUDIOPROCESSOR_H_INCLUDED //============================================================================== /** Base class for audio processing filters or plugins. This is intended to act as a base class of audio filter that is general enough to be wrapped as a VST, AU, RTAS, etc, or used internally. It is also used by the plugin hosting code as the wrapper around an instance of a loaded plugin. Derive your filter class from this base class, and if you're building a plugin, you should implement a global function called createPluginFilter() which creates and returns a new instance of your subclass. */ class JUCE_API AudioProcessor { protected: //============================================================================== /** Constructor. */ AudioProcessor(); public: /** Destructor. */ virtual ~AudioProcessor(); //============================================================================== /** Returns the name of this processor. */ virtual const String getName() const = 0; //============================================================================== /** Called before playback starts, to let the filter prepare itself. The sample rate is the target sample rate, and will remain constant until playback stops. The estimatedSamplesPerBlock value is a HINT about the typical number of samples that will be processed for each callback, but isn't any kind of guarantee. The actual block sizes that the host uses may be different each time the callback happens, and may be more or less than this value. */ virtual void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) = 0; /** Called after playback has stopped, to let the filter free up any resources it no longer needs. */ virtual void releaseResources() = 0; /** Renders the next block. When this method is called, the buffer contains a number of channels which is at least as great as the maximum number of input and output channels that this filter is using. It will be filled with the filter's input data and should be replaced with the filter's output. So for example if your filter has 2 input channels and 4 output channels, then the buffer will contain 4 channels, the first two being filled with the input data. Your filter should read these, do its processing, and replace the contents of all 4 channels with its output. Or if your filter has 5 inputs and 2 outputs, the buffer will have 5 channels, all filled with data, and your filter should overwrite the first 2 of these with its output. But be VERY careful not to write anything to the last 3 channels, as these might be mapped to memory that the host assumes is read-only! Note that if you have more outputs than inputs, then only those channels that correspond to an input channel are guaranteed to contain sensible data - e.g. in the case of 2 inputs and 4 outputs, the first two channels contain the input, but the last two channels may contain garbage, so you should be careful not to let this pass through without being overwritten or cleared. Also note that the buffer may have more channels than are strictly necessary, but you should only read/write from the ones that your filter is supposed to be using. The number of samples in these buffers is NOT guaranteed to be the same for every callback, and may be more or less than the estimated value given to prepareToPlay(). Your code must be able to cope with variable-sized blocks, or you're going to get clicks and crashes! If the filter is receiving a midi input, then the midiMessages array will be filled with the midi messages for this block. Each message's timestamp will indicate the message's time, as a number of samples from the start of the block. Any messages left in the midi buffer when this method has finished are assumed to be the filter's midi output. This means that your filter should be careful to clear any incoming messages from the array if it doesn't want them to be passed-on. Be very careful about what you do in this callback - it's going to be called by the audio thread, so any kind of interaction with the UI is absolutely out of the question. If you change a parameter in here and need to tell your UI to update itself, the best way is probably to inherit from a ChangeBroadcaster, let the UI components register as listeners, and then call sendChangeMessage() inside the processBlock() method to send out an asynchronous message. You could also use the AsyncUpdater class in a similar way. */ virtual void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) = 0; /** Renders the next block when the processor is being bypassed. The default implementation of this method will pass-through any incoming audio, but you may override this method e.g. to add latency compensation to the data to match the processor's latency characteristics. This will avoid situations where bypassing will shift the signal forward in time, possibly creating pre-echo effects and odd timings. Another use for this method would be to cross-fade or morph between the wet (not bypassed) and dry (bypassed) signals. */ virtual void processBlockBypassed (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); //============================================================================== /** Returns the current AudioPlayHead object that should be used to find out the state and position of the playhead. You can ONLY call this from your processBlock() method! Calling it at other times will produce undefined behaviour, as the host may not have any context in which a time would make sense, and some hosts will almost certainly have multithreading issues if it's not called on the audio thread. The AudioPlayHead object that is returned can be used to get the details about the time of the start of the block currently being processed. But do not store this pointer or use it outside of the current audio callback, because the host may delete or re-use it. If the host can't or won't provide any time info, this will return nullptr. */ AudioPlayHead* getPlayHead() const noexcept { return playHead; } //============================================================================== /** Returns the current sample rate. This can be called from your processBlock() method - it's not guaranteed to be valid at any other time, and may return 0 if it's unknown. */ double getSampleRate() const noexcept { return sampleRate; } /** Returns the current typical block size that is being used. This can be called from your processBlock() method - it's not guaranteed to be valid at any other time. Remember it's not the ONLY block size that may be used when calling processBlock, it's just the normal one. The actual block sizes used may be larger or smaller than this, and will vary between successive calls. */ int getBlockSize() const noexcept { return blockSize; } //============================================================================== /** Returns the number of input channels that the host will be sending the filter. If writing a plugin, your configuration macros should specify the number of channels that your filter would prefer to have, and this method lets you know how many the host is actually using. Note that this method is only valid during or after the prepareToPlay() method call. Until that point, the number of channels will be unknown. */ int getNumInputChannels() const noexcept { return numInputChannels; } /** Returns the number of output channels that the host will be sending the filter. If writing a plugin, your configuration macros should specify the number of channels that your filter would prefer to have, and this method lets you know how many the host is actually using. Note that this method is only valid during or after the prepareToPlay() method call. Until that point, the number of channels will be unknown. */ int getNumOutputChannels() const noexcept { return numOutputChannels; } /** Returns a string containing a whitespace-separated list of speaker types corresponding to each input channel. For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs" If the speaker arrangement is unknown, the returned string will be empty. */ const String& getInputSpeakerArrangement() const noexcept { return inputSpeakerArrangement; } /** Returns a string containing a whitespace-separated list of speaker types corresponding to each output channel. For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs" If the speaker arrangement is unknown, the returned string will be empty. */ const String& getOutputSpeakerArrangement() const noexcept { return outputSpeakerArrangement; } //============================================================================== /** Returns the name of one of the processor's input channels. The processor might not supply very useful names for channels, and this might be something like "1", "2", "left", "right", etc. */ virtual const String getInputChannelName (int channelIndex) const = 0; /** Returns the name of one of the processor's output channels. The processor might not supply very useful names for channels, and this might be something like "1", "2", "left", "right", etc. */ virtual const String getOutputChannelName (int channelIndex) const = 0; /** Returns true if the specified channel is part of a stereo pair with its neighbour. */ virtual bool isInputChannelStereoPair (int index) const = 0; /** Returns true if the specified channel is part of a stereo pair with its neighbour. */ virtual bool isOutputChannelStereoPair (int index) const = 0; /** This returns the number of samples delay that the filter imposes on the audio passing through it. The host will call this to find the latency - the filter itself should set this value by calling setLatencySamples() as soon as it can during its initialisation. */ int getLatencySamples() const noexcept { return latencySamples; } /** The filter should call this to set the number of samples delay that it introduces. The filter should call this as soon as it can during initialisation, and can call it later if the value changes. */ void setLatencySamples (int newLatency); /** Returns true if a silent input always produces a silent output. */ virtual bool silenceInProducesSilenceOut() const = 0; /** Returns the length of the filter's tail, in seconds. */ virtual double getTailLengthSeconds() const = 0; /** Returns true if the processor wants midi messages. */ virtual bool acceptsMidi() const = 0; /** Returns true if the processor produces midi messages. */ virtual bool producesMidi() const = 0; //============================================================================== /** This returns a critical section that will automatically be locked while the host is calling the processBlock() method. Use it from your UI or other threads to lock access to variables that are used by the process callback, but obviously be careful not to keep it locked for too long, because that could cause stuttering playback. If you need to do something that'll take a long time and need the processing to stop while it happens, use the suspendProcessing() method instead. @see suspendProcessing */ const CriticalSection& getCallbackLock() const noexcept { return callbackLock; } /** Enables and disables the processing callback. If you need to do something time-consuming on a thread and would like to make sure the audio processing callback doesn't happen until you've finished, use this to disable the callback and re-enable it again afterwards. E.g. @code void loadNewPatch() { suspendProcessing (true); ..do something that takes ages.. suspendProcessing (false); } @endcode If the host tries to make an audio callback while processing is suspended, the filter will return an empty buffer, but won't block the audio thread like it would do if you use the getCallbackLock() critical section to synchronise access. Any code that calls processBlock() should call isSuspended() before doing so, and if the processor is suspended, it should avoid the call and emit silence or whatever is appropriate. @see getCallbackLock */ void suspendProcessing (bool shouldBeSuspended); /** Returns true if processing is currently suspended. @see suspendProcessing */ bool isSuspended() const noexcept { return suspended; } /** A plugin can override this to be told when it should reset any playing voices. The default implementation does nothing, but a host may call this to tell the plugin that it should stop any tails or sounds that have been left running. */ virtual void reset(); //============================================================================== /** Returns true if the processor is being run in an offline mode for rendering. If the processor is being run live on realtime signals, this returns false. If the mode is unknown, this will assume it's realtime and return false. This value may be unreliable until the prepareToPlay() method has been called, and could change each time prepareToPlay() is called. @see setNonRealtime() */ bool isNonRealtime() const noexcept { return nonRealtime; } /** Called by the host to tell this processor whether it's being used in a non-realtime capacity for offline rendering or bouncing. */ virtual void setNonRealtime (bool isNonRealtime) noexcept; //============================================================================== /** Creates the filter's UI. This can return nullptr if you want a UI-less filter, in which case the host may create a generic UI that lets the user twiddle the parameters directly. If you do want to pass back a component, the component should be created and set to the correct size before returning it. If you implement this method, you must also implement the hasEditor() method and make it return true. Remember not to do anything silly like allowing your filter to keep a pointer to the component that gets created - it could be deleted later without any warning, which would make your pointer into a dangler. Use the getActiveEditor() method instead. The correct way to handle the connection between an editor component and its filter is to use something like a ChangeBroadcaster so that the editor can register itself as a listener, and be told when a change occurs. This lets them safely unregister themselves when they are deleted. Here are a few things to bear in mind when writing an editor: - Initially there won't be an editor, until the user opens one, or they might not open one at all. Your filter mustn't rely on it being there. - An editor object may be deleted and a replacement one created again at any time. - It's safe to assume that an editor will be deleted before its filter. @see hasEditor */ virtual AudioProcessorEditor* createEditor() = 0; /** Your filter must override this and return true if it can create an editor component. @see createEditor */ virtual bool hasEditor() const = 0; //============================================================================== /** Returns the active editor, if there is one. Bear in mind this can return nullptr, even if an editor has previously been opened. */ AudioProcessorEditor* getActiveEditor() const noexcept { return activeEditor; } /** Returns the active editor, or if there isn't one, it will create one. This may call createEditor() internally to create the component. */ AudioProcessorEditor* createEditorIfNeeded(); //============================================================================== /** This must return the correct value immediately after the object has been created, and mustn't change the number of parameters later. NOTE! This method will eventually be deprecated! It's recommended that you use the AudioProcessorParameter class instead to manage your parameters. */ virtual int getNumParameters(); /** Returns the name of a particular parameter. NOTE! This method will eventually be deprecated! It's recommended that you use the AudioProcessorParameter class instead to manage your parameters. */ virtual const String getParameterName (int parameterIndex); /** Called by the host to find out the value of one of the filter's parameters. The host will expect the value returned to be between 0 and 1.0. This could be called quite frequently, so try to make your code efficient. It's also likely to be called by non-UI threads, so the code in here should be thread-aware. NOTE! This method will eventually be deprecated! It's recommended that you use the AudioProcessorParameter class instead to manage your parameters. */ virtual float getParameter (int parameterIndex); /** Returns the name of a parameter as a text string with a preferred maximum length. If you want to provide customised short versions of your parameter names that will look better in constrained spaces (e.g. the displays on hardware controller devices or mixing desks) then you should implement this method. If you don't override it, the default implementation will call getParameterText(int), and truncate the result. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getName() instead. */ virtual String getParameterName (int parameterIndex, int maximumStringLength); /** Returns the value of a parameter as a text string. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getText() instead. */ virtual const String getParameterText (int parameterIndex); /** Returns the value of a parameter as a text string with a preferred maximum length. If you want to provide customised short versions of your parameter values that will look better in constrained spaces (e.g. the displays on hardware controller devices or mixing desks) then you should implement this method. If you don't override it, the default implementation will call getParameterText(int), and truncate the result. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getText() instead. */ virtual String getParameterText (int parameterIndex, int maximumStringLength); /** Returns the number of discrete steps that this parameter can represent. The default return value if you don't implement this method is AudioProcessor::getDefaultNumParameterSteps(). If your parameter is boolean, then you may want to make this return 2. The value that is returned may or may not be used, depending on the host. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getNumSteps() instead. */ virtual int getParameterNumSteps (int parameterIndex); /** Returns the default number of steps for a parameter. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getNumSteps() instead. @see getParameterNumSteps */ static int getDefaultNumParameterSteps() noexcept; /** Returns the default value for the parameter. By default, this just returns 0. The value that is returned may or may not be used, depending on the host. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getDefaultValue() instead. */ virtual float getParameterDefaultValue (int parameterIndex); /** Some plugin types may be able to return a label string for a parameter's units. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getLabel() instead. */ virtual String getParameterLabel (int index) const; /** This can be overridden to tell the host that particular parameters operate in the reverse direction. (Not all plugin formats or hosts will actually use this information). NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::isOrientationInverted() instead. */ virtual bool isParameterOrientationInverted (int index) const; /** The host will call this method to change the value of one of the filter's parameters. The host may call this at any time, including during the audio processing callback, so the filter has to process this very fast and avoid blocking. If you want to set the value of a parameter internally, e.g. from your editor component, then don't call this directly - instead, use the setParameterNotifyingHost() method, which will also send a message to the host telling it about the change. If the message isn't sent, the host won't be able to automate your parameters properly. The value passed will be between 0 and 1.0. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::setValue() instead. */ virtual void setParameter (int parameterIndex, float newValue); /** Your filter can call this when it needs to change one of its parameters. This could happen when the editor or some other internal operation changes a parameter. This method will call the setParameter() method to change the value, and will then send a message to the host telling it about the change. Note that to make sure the host correctly handles automation, you should call the beginParameterChangeGesture() and endParameterChangeGesture() methods to tell the host when the user has started and stopped changing the parameter. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::setValueNotifyingHost() instead. */ void setParameterNotifyingHost (int parameterIndex, float newValue); /** Returns true if the host can automate this parameter. By default, this returns true for all parameters. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::isAutomatable() instead. */ virtual bool isParameterAutomatable (int parameterIndex) const; /** Should return true if this parameter is a "meta" parameter. A meta-parameter is a parameter that changes other params. It is used by some hosts (e.g. AudioUnit hosts). By default this returns false. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::isMetaParameter() instead. */ virtual bool isMetaParameter (int parameterIndex) const; /** Sends a signal to the host to tell it that the user is about to start changing this parameter. This allows the host to know when a parameter is actively being held by the user, and it may use this information to help it record automation. If you call this, it must be matched by a later call to endParameterChangeGesture(). NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::beginChangeGesture() instead. */ void beginParameterChangeGesture (int parameterIndex); /** Tells the host that the user has finished changing this parameter. This allows the host to know when a parameter is actively being held by the user, and it may use this information to help it record automation. A call to this method must follow a call to beginParameterChangeGesture(). NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::endChangeGesture() instead. */ void endParameterChangeGesture (int parameterIndex); /** The filter can call this when something (apart from a parameter value) has changed. It sends a hint to the host that something like the program, number of parameters, etc, has changed, and that it should update itself. */ void updateHostDisplay(); //============================================================================== /** Adds a parameter to the list. The parameter object will be managed and deleted automatically by the list when no longer needed. */ void addParameter (AudioProcessorParameter*); /** Returns the current list of parameters. */ const OwnedArray& getParameters() const noexcept; //============================================================================== /** Returns the number of preset programs the filter supports. The value returned must be valid as soon as this object is created, and must not change over its lifetime. This value shouldn't be less than 1. */ virtual int getNumPrograms() = 0; /** Returns the number of the currently active program. */ virtual int getCurrentProgram() = 0; /** Called by the host to change the current program. */ virtual void setCurrentProgram (int index) = 0; /** Must return the name of a given program. */ virtual const String getProgramName (int index) = 0; /** Called by the host to rename a program. */ virtual void changeProgramName (int index, const String& newName) = 0; //============================================================================== /** The host will call this method when it wants to save the filter's internal state. This must copy any info about the filter's state into the block of memory provided, so that the host can store this and later restore it using setStateInformation(). Note that there's also a getCurrentProgramStateInformation() method, which only stores the current program, not the state of the entire filter. See also the helper function copyXmlToBinary() for storing settings as XML. @see getCurrentProgramStateInformation */ virtual void getStateInformation (juce::MemoryBlock& destData) = 0; /** The host will call this method if it wants to save the state of just the filter's current program. Unlike getStateInformation, this should only return the current program's state. Not all hosts support this, and if you don't implement it, the base class method just calls getStateInformation() instead. If you do implement it, be sure to also implement getCurrentProgramStateInformation. @see getStateInformation, setCurrentProgramStateInformation */ virtual void getCurrentProgramStateInformation (juce::MemoryBlock& destData); /** This must restore the filter's state from a block of data previously created using getStateInformation(). Note that there's also a setCurrentProgramStateInformation() method, which tries to restore just the current program, not the state of the entire filter. See also the helper function getXmlFromBinary() for loading settings as XML. @see setCurrentProgramStateInformation */ virtual void setStateInformation (const void* data, int sizeInBytes) = 0; /** The host will call this method if it wants to restore the state of just the filter's current program. Not all hosts support this, and if you don't implement it, the base class method just calls setStateInformation() instead. If you do implement it, be sure to also implement getCurrentProgramStateInformation. @see setStateInformation, getCurrentProgramStateInformation */ virtual void setCurrentProgramStateInformation (const void* data, int sizeInBytes); /** This method is called when the number of input or output channels is changed. */ virtual void numChannelsChanged(); //============================================================================== /** Adds a listener that will be called when an aspect of this processor changes. */ virtual void addListener (AudioProcessorListener* newListener); /** Removes a previously added listener. */ virtual void removeListener (AudioProcessorListener* listenerToRemove); //============================================================================== /** Tells the processor to use this playhead object. The processor will not take ownership of the object, so the caller must delete it when it is no longer being used. */ virtual void setPlayHead (AudioPlayHead* newPlayHead); //============================================================================== /** This is called by the processor to specify its details before being played. */ void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize) noexcept; //============================================================================== /** Not for public use - this is called before deleting an editor component. */ void editorBeingDeleted (AudioProcessorEditor*) noexcept; /** Not for public use - this is called to initialise the processor before playing. */ void setSpeakerArrangement (const String& inputs, const String& outputs); /** Flags to indicate the type of plugin context in which a processor is being used. */ enum WrapperType { wrapperType_Undefined = 0, wrapperType_VST, wrapperType_VST3, wrapperType_AudioUnit, wrapperType_RTAS, wrapperType_AAX, wrapperType_Standalone }; /** When loaded by a plugin wrapper, this flag will be set to indicate the type of plugin within which the processor is running. */ WrapperType wrapperType; //============================================================================== /** Helper function that just converts an xml element into a binary blob. Use this in your filter's getStateInformation() method if you want to store its state as xml. Then use getXmlFromBinary() to reverse this operation and retrieve the XML from a binary blob. */ static void copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& destData); /** Retrieves an XML element that was stored as binary with the copyXmlToBinary() method. This might return nullptr if the data's unsuitable or corrupted. Otherwise it will return an XmlElement object that the caller must delete when no longer needed. */ static XmlElement* getXmlFromBinary (const void* data, int sizeInBytes); /** @internal */ static void JUCE_CALLTYPE setTypeOfNextNewPlugin (WrapperType); protected: /** @internal */ AudioPlayHead* playHead; /** @internal */ void sendParamChangeMessageToListeners (int parameterIndex, float newValue); private: Array listeners; Component::SafePointer activeEditor; double sampleRate; int blockSize, numInputChannels, numOutputChannels, latencySamples; bool suspended, nonRealtime; CriticalSection callbackLock, listenerLock; String inputSpeakerArrangement, outputSpeakerArrangement; OwnedArray managedParameters; AudioProcessorParameter* getParamChecked (int) const noexcept; #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING BigInteger changingParams; #endif AudioProcessorListener* getListenerLocked (int) const noexcept; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor) }; #endif // JUCE_AUDIOPROCESSOR_H_INCLUDED juce_AudioProcessorEditor.cpp000066400000000000000000000030521320201440200361420ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p) { } AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p) { // the filter must be valid.. jassert (p != nullptr); } AudioProcessorEditor::~AudioProcessorEditor() { // if this fails, then the wrapper hasn't called editorBeingDeleted() on the // filter for some reason.. jassert (processor.getActiveEditor() != this); } void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {} int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; } juce_AudioProcessorEditor.h000066400000000000000000000067101320201440200356130ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED #define JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED //============================================================================== /** Base class for the component that acts as the GUI for an AudioProcessor. Derive your editor component from this class, and create an instance of it by overriding the AudioProcessor::createEditor() method. @see AudioProcessor, GenericAudioProcessorEditor */ class JUCE_API AudioProcessorEditor : public Component { protected: //============================================================================== /** Creates an editor for the specified processor. */ AudioProcessorEditor (AudioProcessor&) noexcept; /** Creates an editor for the specified processor. */ AudioProcessorEditor (AudioProcessor*) noexcept; public: /** Destructor. */ ~AudioProcessorEditor(); //============================================================================== /** The AudioProcessor that this editor represents. */ AudioProcessor& processor; /** Returns a pointer to the processor that this editor represents. This method is here to support legacy code, but it's easier to just use the AudioProcessorEditor::processor member variable directly to get this object. */ AudioProcessor* getAudioProcessor() const noexcept { return &processor; } //============================================================================== /** Used by the setParameterHighlighting() method. */ struct ParameterControlHighlightInfo { int parameterIndex; bool isHighlighted; Colour suggestedColour; }; /** Some types of plugin can call this to suggest that the control for a particular parameter should be highlighted. Currently only AAX plugins will call this, and implementing it is optional. */ virtual void setControlHighlight (ParameterControlHighlightInfo); /** Called by certain plug-in wrappers to find out whether a component is used to control a parameter. If the given component represents a particular plugin parameter, then this method should return the index of that parameter. If not, it should return -1. Currently only AAX plugins will call this, and implementing it is optional. */ virtual int getControlParameterIndex (Component&); private: JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) }; #endif // JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED juce_AudioProcessorGraph.cpp000066400000000000000000001510011320201440200357530ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ const int AudioProcessorGraph::midiChannelIndex = 0x1000; //============================================================================== namespace GraphRenderingOps { //============================================================================== struct AudioGraphRenderingOp { AudioGraphRenderingOp() noexcept {} virtual ~AudioGraphRenderingOp() {} virtual void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray& sharedMidiBuffers, const int numSamples) = 0; JUCE_LEAK_DETECTOR (AudioGraphRenderingOp) }; //============================================================================== struct ClearChannelOp : public AudioGraphRenderingOp { ClearChannelOp (const int channel) noexcept : channelNum (channel) {} void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { sharedBufferChans.clear (channelNum, 0, numSamples); } const int channelNum; JUCE_DECLARE_NON_COPYABLE (ClearChannelOp) }; //============================================================================== struct CopyChannelOp : public AudioGraphRenderingOp { CopyChannelOp (const int srcChan, const int dstChan) noexcept : srcChannelNum (srcChan), dstChannelNum (dstChan) {} void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { sharedBufferChans.copyFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); } const int srcChannelNum, dstChannelNum; JUCE_DECLARE_NON_COPYABLE (CopyChannelOp) }; //============================================================================== struct AddChannelOp : public AudioGraphRenderingOp { AddChannelOp (const int srcChan, const int dstChan) noexcept : srcChannelNum (srcChan), dstChannelNum (dstChan) {} void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { sharedBufferChans.addFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); } const int srcChannelNum, dstChannelNum; JUCE_DECLARE_NON_COPYABLE (AddChannelOp) }; //============================================================================== struct ClearMidiBufferOp : public AudioGraphRenderingOp { ClearMidiBufferOp (const int buffer) noexcept : bufferNum (buffer) {} void perform (AudioSampleBuffer&, const OwnedArray& sharedMidiBuffers, const int) { sharedMidiBuffers.getUnchecked (bufferNum)->clear(); } const int bufferNum; JUCE_DECLARE_NON_COPYABLE (ClearMidiBufferOp) }; //============================================================================== struct CopyMidiBufferOp : public AudioGraphRenderingOp { CopyMidiBufferOp (const int srcBuffer, const int dstBuffer) noexcept : srcBufferNum (srcBuffer), dstBufferNum (dstBuffer) {} void perform (AudioSampleBuffer&, const OwnedArray& sharedMidiBuffers, const int) { *sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum); } const int srcBufferNum, dstBufferNum; JUCE_DECLARE_NON_COPYABLE (CopyMidiBufferOp) }; //============================================================================== struct AddMidiBufferOp : public AudioGraphRenderingOp { AddMidiBufferOp (const int srcBuffer, const int dstBuffer) : srcBufferNum (srcBuffer), dstBufferNum (dstBuffer) {} void perform (AudioSampleBuffer&, const OwnedArray& sharedMidiBuffers, const int numSamples) { sharedMidiBuffers.getUnchecked (dstBufferNum) ->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0); } const int srcBufferNum, dstBufferNum; JUCE_DECLARE_NON_COPYABLE (AddMidiBufferOp) }; //============================================================================== struct DelayChannelOp : public AudioGraphRenderingOp { DelayChannelOp (const int chan, const int delaySize) : channel (chan), bufferSize (delaySize + 1), readIndex (0), writeIndex (delaySize) { buffer.calloc ((size_t) bufferSize); } void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { float* data = sharedBufferChans.getWritePointer (channel, 0); for (int i = numSamples; --i >= 0;) { buffer [writeIndex] = *data; *data++ = buffer [readIndex]; if (++readIndex >= bufferSize) readIndex = 0; if (++writeIndex >= bufferSize) writeIndex = 0; } } private: HeapBlock buffer; const int channel, bufferSize; int readIndex, writeIndex; JUCE_DECLARE_NON_COPYABLE (DelayChannelOp) }; //============================================================================== struct ProcessBufferOp : public AudioGraphRenderingOp { ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& n, const Array& audioChannels, const int totalNumChans, const int midiBuffer) : node (n), processor (n->getProcessor()), audioChannelsToUse (audioChannels), totalChans (jmax (1, totalNumChans)), midiBufferToUse (midiBuffer) { channels.calloc ((size_t) totalChans); while (audioChannelsToUse.size() < totalChans) audioChannelsToUse.add (0); } void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray& sharedMidiBuffers, const int numSamples) { for (int i = totalChans; --i >= 0;) channels[i] = sharedBufferChans.getWritePointer (audioChannelsToUse.getUnchecked (i), 0); AudioSampleBuffer buffer (channels, totalChans, numSamples); processor->processBlock (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse)); } const AudioProcessorGraph::Node::Ptr node; AudioProcessor* const processor; private: Array audioChannelsToUse; HeapBlock channels; const int totalChans; const int midiBufferToUse; JUCE_DECLARE_NON_COPYABLE (ProcessBufferOp) }; //============================================================================== /** Used to calculate the correct sequence of rendering ops needed, based on the best re-use of shared buffers at each stage. */ struct RenderingOpSequenceCalculator { RenderingOpSequenceCalculator (AudioProcessorGraph& g, const Array& nodes, Array& renderingOps) : graph (g), orderedNodes (nodes), totalLatency (0) { nodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros channels.add (0); midiNodeIds.add ((uint32) zeroNodeID); for (int i = 0; i < orderedNodes.size(); ++i) { createRenderingOpsForNode (*orderedNodes.getUnchecked(i), renderingOps, i); markAnyUnusedBuffersAsFree (i); } graph.setLatencySamples (totalLatency); } int getNumBuffersNeeded() const noexcept { return nodeIds.size(); } int getNumMidiBuffersNeeded() const noexcept { return midiNodeIds.size(); } private: //============================================================================== AudioProcessorGraph& graph; const Array& orderedNodes; Array channels; Array nodeIds, midiNodeIds; enum { freeNodeID = 0xffffffff, zeroNodeID = 0xfffffffe }; static bool isNodeBusy (uint32 nodeID) noexcept { return nodeID != freeNodeID && nodeID != zeroNodeID; } Array nodeDelayIDs; Array nodeDelays; int totalLatency; int getNodeDelay (const uint32 nodeID) const { return nodeDelays [nodeDelayIDs.indexOf (nodeID)]; } void setNodeDelay (const uint32 nodeID, const int latency) { const int index = nodeDelayIDs.indexOf (nodeID); if (index >= 0) { nodeDelays.set (index, latency); } else { nodeDelayIDs.add (nodeID); nodeDelays.add (latency); } } int getInputLatencyForNode (const uint32 nodeID) const { int maxLatency = 0; for (int i = graph.getNumConnections(); --i >= 0;) { const AudioProcessorGraph::Connection* const c = graph.getConnection (i); if (c->destNodeId == nodeID) maxLatency = jmax (maxLatency, getNodeDelay (c->sourceNodeId)); } return maxLatency; } //============================================================================== void createRenderingOpsForNode (AudioProcessorGraph::Node& node, Array& renderingOps, const int ourRenderingIndex) { AudioProcessor& processor = *node.getProcessor(); const int numIns = processor.getNumInputChannels(); const int numOuts = processor.getNumOutputChannels(); const int totalChans = jmax (numIns, numOuts); Array audioChannelsToUse; int midiBufferToUse = -1; int maxLatency = getInputLatencyForNode (node.nodeId); for (int inputChan = 0; inputChan < numIns; ++inputChan) { // get a list of all the inputs to this node Array sourceNodes; Array sourceOutputChans; for (int i = graph.getNumConnections(); --i >= 0;) { const AudioProcessorGraph::Connection* const c = graph.getConnection (i); if (c->destNodeId == node.nodeId && c->destChannelIndex == inputChan) { sourceNodes.add (c->sourceNodeId); sourceOutputChans.add (c->sourceChannelIndex); } } int bufIndex = -1; if (sourceNodes.size() == 0) { // unconnected input channel if (inputChan >= numOuts) { bufIndex = getReadOnlyEmptyBuffer(); jassert (bufIndex >= 0); } else { bufIndex = getFreeBuffer (false); renderingOps.add (new ClearChannelOp (bufIndex)); } } else if (sourceNodes.size() == 1) { // channel with a straightforward single input.. const uint32 srcNode = sourceNodes.getUnchecked(0); const int srcChan = sourceOutputChans.getUnchecked(0); bufIndex = getBufferContaining (srcNode, srcChan); if (bufIndex < 0) { // if not found, this is probably a feedback loop bufIndex = getReadOnlyEmptyBuffer(); jassert (bufIndex >= 0); } if (inputChan < numOuts && isBufferNeededLater (ourRenderingIndex, inputChan, srcNode, srcChan)) { // can't mess up this channel because it's needed later by another node, so we // need to use a copy of it.. const int newFreeBuffer = getFreeBuffer (false); renderingOps.add (new CopyChannelOp (bufIndex, newFreeBuffer)); bufIndex = newFreeBuffer; } const int nodeDelay = getNodeDelay (srcNode); if (nodeDelay < maxLatency) renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay)); } else { // channel with a mix of several inputs.. // try to find a re-usable channel from our inputs.. int reusableInputIndex = -1; for (int i = 0; i < sourceNodes.size(); ++i) { const int sourceBufIndex = getBufferContaining (sourceNodes.getUnchecked(i), sourceOutputChans.getUnchecked(i)); if (sourceBufIndex >= 0 && ! isBufferNeededLater (ourRenderingIndex, inputChan, sourceNodes.getUnchecked(i), sourceOutputChans.getUnchecked(i))) { // we've found one of our input chans that can be re-used.. reusableInputIndex = i; bufIndex = sourceBufIndex; const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (i)); if (nodeDelay < maxLatency) renderingOps.add (new DelayChannelOp (sourceBufIndex, maxLatency - nodeDelay)); break; } } if (reusableInputIndex < 0) { // can't re-use any of our input chans, so get a new one and copy everything into it.. bufIndex = getFreeBuffer (false); jassert (bufIndex != 0); const int srcIndex = getBufferContaining (sourceNodes.getUnchecked (0), sourceOutputChans.getUnchecked (0)); if (srcIndex < 0) { // if not found, this is probably a feedback loop renderingOps.add (new ClearChannelOp (bufIndex)); } else { renderingOps.add (new CopyChannelOp (srcIndex, bufIndex)); } reusableInputIndex = 0; const int nodeDelay = getNodeDelay (sourceNodes.getFirst()); if (nodeDelay < maxLatency) renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay)); } for (int j = 0; j < sourceNodes.size(); ++j) { if (j != reusableInputIndex) { int srcIndex = getBufferContaining (sourceNodes.getUnchecked(j), sourceOutputChans.getUnchecked(j)); if (srcIndex >= 0) { const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (j)); if (nodeDelay < maxLatency) { if (! isBufferNeededLater (ourRenderingIndex, inputChan, sourceNodes.getUnchecked(j), sourceOutputChans.getUnchecked(j))) { renderingOps.add (new DelayChannelOp (srcIndex, maxLatency - nodeDelay)); } else // buffer is reused elsewhere, can't be delayed { const int bufferToDelay = getFreeBuffer (false); renderingOps.add (new CopyChannelOp (srcIndex, bufferToDelay)); renderingOps.add (new DelayChannelOp (bufferToDelay, maxLatency - nodeDelay)); srcIndex = bufferToDelay; } } renderingOps.add (new AddChannelOp (srcIndex, bufIndex)); } } } } jassert (bufIndex >= 0); audioChannelsToUse.add (bufIndex); if (inputChan < numOuts) markBufferAsContaining (bufIndex, node.nodeId, inputChan); } for (int outputChan = numIns; outputChan < numOuts; ++outputChan) { const int bufIndex = getFreeBuffer (false); jassert (bufIndex != 0); audioChannelsToUse.add (bufIndex); markBufferAsContaining (bufIndex, node.nodeId, outputChan); } // Now the same thing for midi.. Array midiSourceNodes; for (int i = graph.getNumConnections(); --i >= 0;) { const AudioProcessorGraph::Connection* const c = graph.getConnection (i); if (c->destNodeId == node.nodeId && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex) midiSourceNodes.add (c->sourceNodeId); } if (midiSourceNodes.size() == 0) { // No midi inputs.. midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi if (processor.acceptsMidi() || processor.producesMidi()) renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); } else if (midiSourceNodes.size() == 1) { // One midi input.. midiBufferToUse = getBufferContaining (midiSourceNodes.getUnchecked(0), AudioProcessorGraph::midiChannelIndex); if (midiBufferToUse >= 0) { if (isBufferNeededLater (ourRenderingIndex, AudioProcessorGraph::midiChannelIndex, midiSourceNodes.getUnchecked(0), AudioProcessorGraph::midiChannelIndex)) { // can't mess up this channel because it's needed later by another node, so we // need to use a copy of it.. const int newFreeBuffer = getFreeBuffer (true); renderingOps.add (new CopyMidiBufferOp (midiBufferToUse, newFreeBuffer)); midiBufferToUse = newFreeBuffer; } } else { // probably a feedback loop, so just use an empty one.. midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi } } else { // More than one midi input being mixed.. int reusableInputIndex = -1; for (int i = 0; i < midiSourceNodes.size(); ++i) { const int sourceBufIndex = getBufferContaining (midiSourceNodes.getUnchecked(i), AudioProcessorGraph::midiChannelIndex); if (sourceBufIndex >= 0 && ! isBufferNeededLater (ourRenderingIndex, AudioProcessorGraph::midiChannelIndex, midiSourceNodes.getUnchecked(i), AudioProcessorGraph::midiChannelIndex)) { // we've found one of our input buffers that can be re-used.. reusableInputIndex = i; midiBufferToUse = sourceBufIndex; break; } } if (reusableInputIndex < 0) { // can't re-use any of our input buffers, so get a new one and copy everything into it.. midiBufferToUse = getFreeBuffer (true); jassert (midiBufferToUse >= 0); const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(0), AudioProcessorGraph::midiChannelIndex); if (srcIndex >= 0) renderingOps.add (new CopyMidiBufferOp (srcIndex, midiBufferToUse)); else renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); reusableInputIndex = 0; } for (int j = 0; j < midiSourceNodes.size(); ++j) { if (j != reusableInputIndex) { const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(j), AudioProcessorGraph::midiChannelIndex); if (srcIndex >= 0) renderingOps.add (new AddMidiBufferOp (srcIndex, midiBufferToUse)); } } } if (processor.producesMidi()) markBufferAsContaining (midiBufferToUse, node.nodeId, AudioProcessorGraph::midiChannelIndex); setNodeDelay (node.nodeId, maxLatency + processor.getLatencySamples()); if (numOuts == 0) totalLatency = maxLatency; renderingOps.add (new ProcessBufferOp (&node, audioChannelsToUse, totalChans, midiBufferToUse)); } //============================================================================== int getFreeBuffer (const bool forMidi) { if (forMidi) { for (int i = 1; i < midiNodeIds.size(); ++i) if (midiNodeIds.getUnchecked(i) == freeNodeID) return i; midiNodeIds.add ((uint32) freeNodeID); return midiNodeIds.size() - 1; } else { for (int i = 1; i < nodeIds.size(); ++i) if (nodeIds.getUnchecked(i) == freeNodeID) return i; nodeIds.add ((uint32) freeNodeID); channels.add (0); return nodeIds.size() - 1; } } int getReadOnlyEmptyBuffer() const noexcept { return 0; } int getBufferContaining (const uint32 nodeId, const int outputChannel) const noexcept { if (outputChannel == AudioProcessorGraph::midiChannelIndex) { for (int i = midiNodeIds.size(); --i >= 0;) if (midiNodeIds.getUnchecked(i) == nodeId) return i; } else { for (int i = nodeIds.size(); --i >= 0;) if (nodeIds.getUnchecked(i) == nodeId && channels.getUnchecked(i) == outputChannel) return i; } return -1; } void markAnyUnusedBuffersAsFree (const int stepIndex) { for (int i = 0; i < nodeIds.size(); ++i) { if (isNodeBusy (nodeIds.getUnchecked(i)) && ! isBufferNeededLater (stepIndex, -1, nodeIds.getUnchecked(i), channels.getUnchecked(i))) { nodeIds.set (i, (uint32) freeNodeID); } } for (int i = 0; i < midiNodeIds.size(); ++i) { if (isNodeBusy (midiNodeIds.getUnchecked(i)) && ! isBufferNeededLater (stepIndex, -1, midiNodeIds.getUnchecked(i), AudioProcessorGraph::midiChannelIndex)) { midiNodeIds.set (i, (uint32) freeNodeID); } } } bool isBufferNeededLater (int stepIndexToSearchFrom, int inputChannelOfIndexToIgnore, const uint32 nodeId, const int outputChanIndex) const { while (stepIndexToSearchFrom < orderedNodes.size()) { const AudioProcessorGraph::Node* const node = (const AudioProcessorGraph::Node*) orderedNodes.getUnchecked (stepIndexToSearchFrom); if (outputChanIndex == AudioProcessorGraph::midiChannelIndex) { if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex && graph.getConnectionBetween (nodeId, AudioProcessorGraph::midiChannelIndex, node->nodeId, AudioProcessorGraph::midiChannelIndex) != nullptr) return true; } else { for (int i = 0; i < node->getProcessor()->getNumInputChannels(); ++i) if (i != inputChannelOfIndexToIgnore && graph.getConnectionBetween (nodeId, outputChanIndex, node->nodeId, i) != nullptr) return true; } inputChannelOfIndexToIgnore = -1; ++stepIndexToSearchFrom; } return false; } void markBufferAsContaining (int bufferNum, uint32 nodeId, int outputIndex) { if (outputIndex == AudioProcessorGraph::midiChannelIndex) { jassert (bufferNum > 0 && bufferNum < midiNodeIds.size()); midiNodeIds.set (bufferNum, nodeId); } else { jassert (bufferNum >= 0 && bufferNum < nodeIds.size()); nodeIds.set (bufferNum, nodeId); channels.set (bufferNum, outputIndex); } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RenderingOpSequenceCalculator) }; //============================================================================== // Holds a fast lookup table for checking which nodes are inputs to others. class ConnectionLookupTable { public: explicit ConnectionLookupTable (const OwnedArray& connections) { for (int i = 0; i < connections.size(); ++i) { const AudioProcessorGraph::Connection* const c = connections.getUnchecked(i); int index; Entry* entry = findEntry (c->destNodeId, index); if (entry == nullptr) { entry = new Entry (c->destNodeId); entries.insert (index, entry); } entry->srcNodes.add (c->sourceNodeId); } } bool isAnInputTo (const uint32 possibleInputId, const uint32 possibleDestinationId) const noexcept { return isAnInputToRecursive (possibleInputId, possibleDestinationId, entries.size()); } private: //============================================================================== struct Entry { explicit Entry (const uint32 destNodeId_) noexcept : destNodeId (destNodeId_) {} const uint32 destNodeId; SortedSet srcNodes; JUCE_DECLARE_NON_COPYABLE (Entry) }; OwnedArray entries; bool isAnInputToRecursive (const uint32 possibleInputId, const uint32 possibleDestinationId, int recursionCheck) const noexcept { int index; if (const Entry* const entry = findEntry (possibleDestinationId, index)) { const SortedSet& srcNodes = entry->srcNodes; if (srcNodes.contains (possibleInputId)) return true; if (--recursionCheck >= 0) { for (int i = 0; i < srcNodes.size(); ++i) if (isAnInputToRecursive (possibleInputId, srcNodes.getUnchecked(i), recursionCheck)) return true; } } return false; } Entry* findEntry (const uint32 destNodeId, int& insertIndex) const noexcept { Entry* result = nullptr; int start = 0; int end = entries.size(); for (;;) { if (start >= end) { break; } else if (destNodeId == entries.getUnchecked (start)->destNodeId) { result = entries.getUnchecked (start); break; } else { const int halfway = (start + end) / 2; if (halfway == start) { if (destNodeId >= entries.getUnchecked (halfway)->destNodeId) ++start; break; } else if (destNodeId >= entries.getUnchecked (halfway)->destNodeId) start = halfway; else end = halfway; } } insertIndex = start; return result; } JUCE_DECLARE_NON_COPYABLE (ConnectionLookupTable) }; //============================================================================== struct ConnectionSorter { static int compareElements (const AudioProcessorGraph::Connection* const first, const AudioProcessorGraph::Connection* const second) noexcept { if (first->sourceNodeId < second->sourceNodeId) return -1; if (first->sourceNodeId > second->sourceNodeId) return 1; if (first->destNodeId < second->destNodeId) return -1; if (first->destNodeId > second->destNodeId) return 1; if (first->sourceChannelIndex < second->sourceChannelIndex) return -1; if (first->sourceChannelIndex > second->sourceChannelIndex) return 1; if (first->destChannelIndex < second->destChannelIndex) return -1; if (first->destChannelIndex > second->destChannelIndex) return 1; return 0; } }; } //============================================================================== AudioProcessorGraph::Connection::Connection (const uint32 sourceID, const int sourceChannel, const uint32 destID, const int destChannel) noexcept : sourceNodeId (sourceID), sourceChannelIndex (sourceChannel), destNodeId (destID), destChannelIndex (destChannel) { } //============================================================================== AudioProcessorGraph::Node::Node (const uint32 nodeID, AudioProcessor* const p) noexcept : nodeId (nodeID), processor (p), isPrepared (false) { jassert (processor != nullptr); } void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, AudioProcessorGraph* const graph) { if (! isPrepared) { isPrepared = true; setParentGraph (graph); processor->setPlayConfigDetails (processor->getNumInputChannels(), processor->getNumOutputChannels(), sampleRate, blockSize); processor->prepareToPlay (sampleRate, blockSize); } } void AudioProcessorGraph::Node::unprepare() { if (isPrepared) { isPrepared = false; processor->releaseResources(); } } void AudioProcessorGraph::Node::setParentGraph (AudioProcessorGraph* const graph) const { if (AudioProcessorGraph::AudioGraphIOProcessor* const ioProc = dynamic_cast (processor.get())) ioProc->setParentGraph (graph); } //============================================================================== AudioProcessorGraph::AudioProcessorGraph() : lastNodeId (0), currentAudioInputBuffer (nullptr), currentMidiInputBuffer (nullptr) { } AudioProcessorGraph::~AudioProcessorGraph() { clearRenderingSequence(); clear(); } const String AudioProcessorGraph::getName() const { return "Audio Graph"; } //============================================================================== void AudioProcessorGraph::clear() { nodes.clear(); connections.clear(); triggerAsyncUpdate(); } AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const { for (int i = nodes.size(); --i >= 0;) if (nodes.getUnchecked(i)->nodeId == nodeId) return nodes.getUnchecked(i); return nullptr; } AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, uint32 nodeId) { if (newProcessor == nullptr || newProcessor == this) { jassertfalse; return nullptr; } for (int i = nodes.size(); --i >= 0;) { if (nodes.getUnchecked(i)->getProcessor() == newProcessor) { jassertfalse; // Cannot add the same object to the graph twice! return nullptr; } } if (nodeId == 0) { nodeId = ++lastNodeId; } else { // you can't add a node with an id that already exists in the graph.. jassert (getNodeForId (nodeId) == nullptr); removeNode (nodeId); if (nodeId > lastNodeId) lastNodeId = nodeId; } newProcessor->setPlayHead (getPlayHead()); Node* const n = new Node (nodeId, newProcessor); nodes.add (n); triggerAsyncUpdate(); n->setParentGraph (this); return n; } bool AudioProcessorGraph::removeNode (const uint32 nodeId) { disconnectNode (nodeId); for (int i = nodes.size(); --i >= 0;) { if (nodes.getUnchecked(i)->nodeId == nodeId) { nodes.remove (i); triggerAsyncUpdate(); return true; } } return false; } //============================================================================== const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId, const int sourceChannelIndex, const uint32 destNodeId, const int destChannelIndex) const { const Connection c (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex); GraphRenderingOps::ConnectionSorter sorter; return connections [connections.indexOfSorted (sorter, &c)]; } bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId, const uint32 possibleDestNodeId) const { for (int i = connections.size(); --i >= 0;) { const Connection* const c = connections.getUnchecked(i); if (c->sourceNodeId == possibleSourceNodeId && c->destNodeId == possibleDestNodeId) { return true; } } return false; } bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId, const int sourceChannelIndex, const uint32 destNodeId, const int destChannelIndex) const { if (sourceChannelIndex < 0 || destChannelIndex < 0 || sourceNodeId == destNodeId || (destChannelIndex == midiChannelIndex) != (sourceChannelIndex == midiChannelIndex)) return false; const Node* const source = getNodeForId (sourceNodeId); if (source == nullptr || (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels()) || (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi())) return false; const Node* const dest = getNodeForId (destNodeId); if (dest == nullptr || (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels()) || (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi())) return false; return getConnectionBetween (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex) == nullptr; } bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId, const int sourceChannelIndex, const uint32 destNodeId, const int destChannelIndex) { if (! canConnect (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex)) return false; GraphRenderingOps::ConnectionSorter sorter; connections.addSorted (sorter, new Connection (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex)); triggerAsyncUpdate(); return true; } void AudioProcessorGraph::removeConnection (const int index) { connections.remove (index); triggerAsyncUpdate(); } bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, const uint32 destNodeId, const int destChannelIndex) { bool doneAnything = false; for (int i = connections.size(); --i >= 0;) { const Connection* const c = connections.getUnchecked(i); if (c->sourceNodeId == sourceNodeId && c->destNodeId == destNodeId && c->sourceChannelIndex == sourceChannelIndex && c->destChannelIndex == destChannelIndex) { removeConnection (i); doneAnything = true; } } return doneAnything; } bool AudioProcessorGraph::disconnectNode (const uint32 nodeId) { bool doneAnything = false; for (int i = connections.size(); --i >= 0;) { const Connection* const c = connections.getUnchecked(i); if (c->sourceNodeId == nodeId || c->destNodeId == nodeId) { removeConnection (i); doneAnything = true; } } return doneAnything; } bool AudioProcessorGraph::isConnectionLegal (const Connection* const c) const { jassert (c != nullptr); const Node* const source = getNodeForId (c->sourceNodeId); const Node* const dest = getNodeForId (c->destNodeId); return source != nullptr && dest != nullptr && (c->sourceChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->sourceChannelIndex, source->processor->getNumOutputChannels()) : source->processor->producesMidi()) && (c->destChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->destChannelIndex, dest->processor->getNumInputChannels()) : dest->processor->acceptsMidi()); } bool AudioProcessorGraph::removeIllegalConnections() { bool doneAnything = false; for (int i = connections.size(); --i >= 0;) { if (! isConnectionLegal (connections.getUnchecked(i))) { removeConnection (i); doneAnything = true; } } return doneAnything; } //============================================================================== static void deleteRenderOpArray (Array& ops) { for (int i = ops.size(); --i >= 0;) delete static_cast (ops.getUnchecked(i)); } void AudioProcessorGraph::clearRenderingSequence() { Array oldOps; { const ScopedLock sl (getCallbackLock()); renderingOps.swapWith (oldOps); } deleteRenderOpArray (oldOps); } bool AudioProcessorGraph::isAnInputTo (const uint32 possibleInputId, const uint32 possibleDestinationId, const int recursionCheck) const { if (recursionCheck > 0) { for (int i = connections.size(); --i >= 0;) { const AudioProcessorGraph::Connection* const c = connections.getUnchecked (i); if (c->destNodeId == possibleDestinationId && (c->sourceNodeId == possibleInputId || isAnInputTo (possibleInputId, c->sourceNodeId, recursionCheck - 1))) return true; } } return false; } void AudioProcessorGraph::buildRenderingSequence() { Array newRenderingOps; int numRenderingBuffersNeeded = 2; int numMidiBuffersNeeded = 1; { MessageManagerLock mml; Array orderedNodes; { const GraphRenderingOps::ConnectionLookupTable table (connections); for (int i = 0; i < nodes.size(); ++i) { Node* const node = nodes.getUnchecked(i); node->prepare (getSampleRate(), getBlockSize(), this); int j = 0; for (; j < orderedNodes.size(); ++j) if (table.isAnInputTo (node->nodeId, ((Node*) orderedNodes.getUnchecked(j))->nodeId)) break; orderedNodes.insert (j, node); } } GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps); numRenderingBuffersNeeded = calculator.getNumBuffersNeeded(); numMidiBuffersNeeded = calculator.getNumMidiBuffersNeeded(); } { // swap over to the new rendering sequence.. const ScopedLock sl (getCallbackLock()); renderingBuffers.setSize (numRenderingBuffersNeeded, getBlockSize()); renderingBuffers.clear(); for (int i = midiBuffers.size(); --i >= 0;) midiBuffers.getUnchecked(i)->clear(); while (midiBuffers.size() < numMidiBuffersNeeded) midiBuffers.add (new MidiBuffer()); renderingOps.swapWith (newRenderingOps); } // delete the old ones.. deleteRenderOpArray (newRenderingOps); } void AudioProcessorGraph::handleAsyncUpdate() { buildRenderingSequence(); } //============================================================================== void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int estimatedSamplesPerBlock) { currentAudioInputBuffer = nullptr; currentAudioOutputBuffer.setSize (jmax (1, getNumOutputChannels()), estimatedSamplesPerBlock); currentMidiInputBuffer = nullptr; currentMidiOutputBuffer.clear(); clearRenderingSequence(); buildRenderingSequence(); } void AudioProcessorGraph::releaseResources() { for (int i = 0; i < nodes.size(); ++i) nodes.getUnchecked(i)->unprepare(); renderingBuffers.setSize (1, 1); midiBuffers.clear(); currentAudioInputBuffer = nullptr; currentAudioOutputBuffer.setSize (1, 1); currentMidiInputBuffer = nullptr; currentMidiOutputBuffer.clear(); } void AudioProcessorGraph::reset() { const ScopedLock sl (getCallbackLock()); for (int i = 0; i < nodes.size(); ++i) nodes.getUnchecked(i)->getProcessor()->reset(); } void AudioProcessorGraph::setNonRealtime (bool isProcessingNonRealtime) noexcept { const ScopedLock sl (getCallbackLock()); for (int i = 0; i < nodes.size(); ++i) nodes.getUnchecked(i)->getProcessor()->setNonRealtime (isProcessingNonRealtime); } void AudioProcessorGraph::setPlayHead (AudioPlayHead* audioPlayHead) { const ScopedLock sl (getCallbackLock()); AudioProcessor::setPlayHead (audioPlayHead); for (int i = 0; i < nodes.size(); ++i) nodes.getUnchecked(i)->getProcessor()->setPlayHead (audioPlayHead); } void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { const int numSamples = buffer.getNumSamples(); currentAudioInputBuffer = &buffer; currentAudioOutputBuffer.setSize (jmax (1, buffer.getNumChannels()), numSamples); currentAudioOutputBuffer.clear(); currentMidiInputBuffer = &midiMessages; currentMidiOutputBuffer.clear(); for (int i = 0; i < renderingOps.size(); ++i) { GraphRenderingOps::AudioGraphRenderingOp* const op = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); op->perform (renderingBuffers, midiBuffers, numSamples); } for (int i = 0; i < buffer.getNumChannels(); ++i) buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples); midiMessages.clear(); midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0); } const String AudioProcessorGraph::getInputChannelName (int channelIndex) const { return "Input " + String (channelIndex + 1); } const String AudioProcessorGraph::getOutputChannelName (int channelIndex) const { return "Output " + String (channelIndex + 1); } bool AudioProcessorGraph::isInputChannelStereoPair (int) const { return true; } bool AudioProcessorGraph::isOutputChannelStereoPair (int) const { return true; } bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; } double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } bool AudioProcessorGraph::acceptsMidi() const { return true; } bool AudioProcessorGraph::producesMidi() const { return true; } void AudioProcessorGraph::getStateInformation (juce::MemoryBlock&) {} void AudioProcessorGraph::setStateInformation (const void*, int) {} //============================================================================== AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType deviceType) : type (deviceType), graph (nullptr) { } AudioProcessorGraph::AudioGraphIOProcessor::~AudioGraphIOProcessor() { } const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const { switch (type) { case audioOutputNode: return "Audio Output"; case audioInputNode: return "Audio Input"; case midiOutputNode: return "Midi Output"; case midiInputNode: return "Midi Input"; default: break; } return String(); } void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const { d.name = getName(); d.uid = d.name.hashCode(); d.category = "I/O devices"; d.pluginFormatName = "Internal"; d.manufacturerName = "ROLI Ltd."; d.version = "1.0"; d.isInstrument = false; d.numInputChannels = getNumInputChannels(); if (type == audioOutputNode && graph != nullptr) d.numInputChannels = graph->getNumInputChannels(); d.numOutputChannels = getNumOutputChannels(); if (type == audioInputNode && graph != nullptr) d.numOutputChannels = graph->getNumOutputChannels(); } void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int) { jassert (graph != nullptr); } void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources() { } void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { jassert (graph != nullptr); switch (type) { case audioOutputNode: { for (int i = jmin (graph->currentAudioOutputBuffer.getNumChannels(), buffer.getNumChannels()); --i >= 0;) { graph->currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples()); } break; } case audioInputNode: { for (int i = jmin (graph->currentAudioInputBuffer->getNumChannels(), buffer.getNumChannels()); --i >= 0;) { buffer.copyFrom (i, 0, *graph->currentAudioInputBuffer, i, 0, buffer.getNumSamples()); } break; } case midiOutputNode: graph->currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0); break; case midiInputNode: midiMessages.addEvents (*graph->currentMidiInputBuffer, 0, buffer.getNumSamples(), 0); break; default: break; } } bool AudioProcessorGraph::AudioGraphIOProcessor::silenceInProducesSilenceOut() const { return isOutput(); } double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const { return 0; } bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const { return type == midiOutputNode; } bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const { return type == midiInputNode; } const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (int channelIndex) const { switch (type) { case audioOutputNode: return "Output " + String (channelIndex + 1); case midiOutputNode: return "Midi Output"; default: break; } return String(); } const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (int channelIndex) const { switch (type) { case audioInputNode: return "Input " + String (channelIndex + 1); case midiInputNode: return "Midi Input"; default: break; } return String(); } bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const { return type == audioInputNode || type == audioOutputNode; } bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int index) const { return isInputChannelStereoPair (index); } bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const noexcept { return type == audioInputNode || type == midiInputNode; } bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const noexcept { return type == audioOutputNode || type == midiOutputNode; } bool AudioProcessorGraph::AudioGraphIOProcessor::hasEditor() const { return false; } AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() { return nullptr; } int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; } int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; } void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { } const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String(); } void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) {} void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (juce::MemoryBlock&) {} void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int) {} void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph) { graph = newGraph; if (graph != nullptr) { setPlayConfigDetails (type == audioOutputNode ? graph->getNumOutputChannels() : 0, type == audioInputNode ? graph->getNumInputChannels() : 0, getSampleRate(), getBlockSize()); updateHostDisplay(); } } juce_AudioProcessorGraph.h000066400000000000000000000417161320201440200354330ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED #define JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED //============================================================================== /** A type of AudioProcessor which plays back a graph of other AudioProcessors. Use one of these objects if you want to wire-up a set of AudioProcessors and play back the result. Processors can be added to the graph as "nodes" using addNode(), and once added, you can connect any of their input or output channels to other nodes using addConnection(). To play back a graph through an audio device, you might want to use an AudioProcessorPlayer object. */ class JUCE_API AudioProcessorGraph : public AudioProcessor, private AsyncUpdater { public: //============================================================================== /** Creates an empty graph. */ AudioProcessorGraph(); /** Destructor. Any processor objects that have been added to the graph will also be deleted. */ ~AudioProcessorGraph(); //============================================================================== /** Represents one of the nodes, or processors, in an AudioProcessorGraph. To create a node, call AudioProcessorGraph::addNode(). */ class JUCE_API Node : public ReferenceCountedObject { public: //============================================================================== /** The ID number assigned to this node. This is assigned by the graph that owns it, and can't be changed. */ const uint32 nodeId; /** The actual processor object that this node represents. */ AudioProcessor* getProcessor() const noexcept { return processor; } /** A set of user-definable properties that are associated with this node. This can be used to attach values to the node for whatever purpose seems useful. For example, you might store an x and y position if your application is displaying the nodes on-screen. */ NamedValueSet properties; //============================================================================== /** A convenient typedef for referring to a pointer to a node object. */ typedef ReferenceCountedObjectPtr Ptr; private: //============================================================================== friend class AudioProcessorGraph; const ScopedPointer processor; bool isPrepared; Node (uint32 nodeId, AudioProcessor*) noexcept; void setParentGraph (AudioProcessorGraph*) const; void prepare (double sampleRate, int blockSize, AudioProcessorGraph*); void unprepare(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node) }; //============================================================================== /** Represents a connection between two channels of two nodes in an AudioProcessorGraph. To create a connection, use AudioProcessorGraph::addConnection(). */ struct JUCE_API Connection { public: //============================================================================== Connection (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex) noexcept; //============================================================================== /** The ID number of the node which is the input source for this connection. @see AudioProcessorGraph::getNodeForId */ uint32 sourceNodeId; /** The index of the output channel of the source node from which this connection takes its data. If this value is the special number AudioProcessorGraph::midiChannelIndex, then it is referring to the source node's midi output. Otherwise, it is the zero-based index of an audio output channel in the source node. */ int sourceChannelIndex; /** The ID number of the node which is the destination for this connection. @see AudioProcessorGraph::getNodeForId */ uint32 destNodeId; /** The index of the input channel of the destination node to which this connection delivers its data. If this value is the special number AudioProcessorGraph::midiChannelIndex, then it is referring to the destination node's midi input. Otherwise, it is the zero-based index of an audio input channel in the destination node. */ int destChannelIndex; private: //============================================================================== JUCE_LEAK_DETECTOR (Connection) }; //============================================================================== /** Deletes all nodes and connections from this graph. Any processor objects in the graph will be deleted. */ void clear(); /** Returns the number of nodes in the graph. */ int getNumNodes() const noexcept { return nodes.size(); } /** Returns a pointer to one of the nodes in the graph. This will return nullptr if the index is out of range. @see getNodeForId */ Node* getNode (const int index) const noexcept { return nodes [index]; } /** Searches the graph for a node with the given ID number and returns it. If no such node was found, this returns nullptr. @see getNode */ Node* getNodeForId (const uint32 nodeId) const; /** Adds a node to the graph. This creates a new node in the graph, for the specified processor. Once you have added a processor to the graph, the graph owns it and will delete it later when it is no longer needed. The optional nodeId parameter lets you specify an ID to use for the node, but if the value is already in use, this new node will overwrite the old one. If this succeeds, it returns a pointer to the newly-created node. */ Node* addNode (AudioProcessor* newProcessor, uint32 nodeId = 0); /** Deletes a node within the graph which has the specified ID. This will also delete any connections that are attached to this node. */ bool removeNode (uint32 nodeId); //============================================================================== /** Returns the number of connections in the graph. */ int getNumConnections() const { return connections.size(); } /** Returns a pointer to one of the connections in the graph. */ const Connection* getConnection (int index) const { return connections [index]; } /** Searches for a connection between some specified channels. If no such connection is found, this returns nullptr. */ const Connection* getConnectionBetween (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex) const; /** Returns true if there is a connection between any of the channels of two specified nodes. */ bool isConnected (uint32 possibleSourceNodeId, uint32 possibleDestNodeId) const; /** Returns true if it would be legal to connect the specified points. */ bool canConnect (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex) const; /** Attempts to connect two specified channels of two nodes. If this isn't allowed (e.g. because you're trying to connect a midi channel to an audio one or other such nonsense), then it'll return false. */ bool addConnection (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex); /** Deletes the connection with the specified index. */ void removeConnection (int index); /** Deletes any connection between two specified points. Returns true if a connection was actually deleted. */ bool removeConnection (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex); /** Removes all connections from the specified node. */ bool disconnectNode (uint32 nodeId); /** Returns true if the given connection's channel numbers map on to valid channels at each end. Even if a connection is valid when created, its status could change if a node changes its channel config. */ bool isConnectionLegal (const Connection* connection) const; /** Performs a sanity checks of all the connections. This might be useful if some of the processors are doing things like changing their channel counts, which could render some connections obsolete. */ bool removeIllegalConnections(); //============================================================================== /** A special number that represents the midi channel of a node. This is used as a channel index value if you want to refer to the midi input or output instead of an audio channel. */ static const int midiChannelIndex; //============================================================================== /** A special type of AudioProcessor that can live inside an AudioProcessorGraph in order to use the audio that comes into and out of the graph itself. If you create an AudioGraphIOProcessor in "input" mode, it will act as a node in the graph which delivers the audio that is coming into the parent graph. This allows you to stream the data to other nodes and process the incoming audio. Likewise, one of these in "output" mode can be sent data which it will add to the sum of data being sent to the graph's output. @see AudioProcessorGraph */ class JUCE_API AudioGraphIOProcessor : public AudioPluginInstance { public: /** Specifies the mode in which this processor will operate. */ enum IODeviceType { audioInputNode, /**< In this mode, the processor has output channels representing all the audio input channels that are coming into its parent audio graph. */ audioOutputNode, /**< In this mode, the processor has input channels representing all the audio output channels that are going out of its parent audio graph. */ midiInputNode, /**< In this mode, the processor has a midi output which delivers the same midi data that is arriving at its parent graph. */ midiOutputNode /**< In this mode, the processor has a midi input and any data sent to it will be passed out of the parent graph. */ }; //============================================================================== /** Returns the mode of this processor. */ IODeviceType getType() const noexcept { return type; } /** Returns the parent graph to which this processor belongs, or nullptr if it hasn't yet been added to one. */ AudioProcessorGraph* getParentGraph() const noexcept { return graph; } /** True if this is an audio or midi input. */ bool isInput() const noexcept; /** True if this is an audio or midi output. */ bool isOutput() const noexcept; //============================================================================== AudioGraphIOProcessor (const IODeviceType type); ~AudioGraphIOProcessor(); const String getName() const override; void fillInPluginDescription (PluginDescription&) const override; void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override; void releaseResources() override; void processBlock (AudioSampleBuffer&, MidiBuffer&) override; const String getInputChannelName (int channelIndex) const override; const String getOutputChannelName (int channelIndex) const override; bool isInputChannelStereoPair (int index) const override; bool isOutputChannelStereoPair (int index) const override; bool silenceInProducesSilenceOut() const override; double getTailLengthSeconds() const override; bool acceptsMidi() const override; bool producesMidi() const override; bool hasEditor() const override; AudioProcessorEditor* createEditor() override; int getNumPrograms() override; int getCurrentProgram() override; void setCurrentProgram (int) override; const String getProgramName (int) override; void changeProgramName (int, const String&) override; void getStateInformation (juce::MemoryBlock& destData) override; void setStateInformation (const void* data, int sizeInBytes) override; /** @internal */ void setParentGraph (AudioProcessorGraph*); private: const IODeviceType type; AudioProcessorGraph* graph; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor) }; //============================================================================== const String getName() const override; void prepareToPlay (double, int) override; void releaseResources() override; void processBlock (AudioSampleBuffer&, MidiBuffer&) override; void reset() override; void setNonRealtime (bool) noexcept override; void setPlayHead (AudioPlayHead*) override; const String getInputChannelName (int) const override; const String getOutputChannelName (int) const override; bool isInputChannelStereoPair (int) const override; bool isOutputChannelStereoPair (int) const override; bool silenceInProducesSilenceOut() const override; double getTailLengthSeconds() const override; bool acceptsMidi() const override; bool producesMidi() const override; bool hasEditor() const override { return false; } AudioProcessorEditor* createEditor() override { return nullptr; } int getNumPrograms() override { return 0; } int getCurrentProgram() override { return 0; } void setCurrentProgram (int) override { } const String getProgramName (int) override { return String(); } void changeProgramName (int, const String&) override { } void getStateInformation (juce::MemoryBlock&) override; void setStateInformation (const void* data, int sizeInBytes) override; private: //============================================================================== ReferenceCountedArray nodes; OwnedArray connections; uint32 lastNodeId; AudioSampleBuffer renderingBuffers; OwnedArray midiBuffers; Array renderingOps; friend class AudioGraphIOProcessor; AudioSampleBuffer* currentAudioInputBuffer; AudioSampleBuffer currentAudioOutputBuffer; MidiBuffer* currentMidiInputBuffer; MidiBuffer currentMidiOutputBuffer; void handleAsyncUpdate() override; void clearRenderingSequence(); void buildRenderingSequence(); bool isAnInputTo (uint32 possibleInputId, uint32 possibleDestinationId, int recursionCheck) const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph) }; #endif // JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED juce_AudioProcessorListener.h000066400000000000000000000117321320201440200361520ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED #define JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED //============================================================================== /** Base class for listeners that want to know about changes to an AudioProcessor. Use AudioProcessor::addListener() to register your listener with an AudioProcessor. @see AudioProcessor */ class JUCE_API AudioProcessorListener { public: //============================================================================== /** Destructor. */ virtual ~AudioProcessorListener() {} //============================================================================== /** Receives a callback when a parameter is changed. IMPORTANT NOTE: this will be called synchronously when a parameter changes, and many audio processors will change their parameter during their audio callback. This means that not only has your handler code got to be completely thread-safe, but it's also got to be VERY fast, and avoid blocking. If you need to handle this event on your message thread, use this callback to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to on the message thread. */ virtual void audioProcessorParameterChanged (AudioProcessor* processor, int parameterIndex, float newValue) = 0; /** Called to indicate that something else in the plugin has changed, like its program, number of parameters, etc. IMPORTANT NOTE: this will be called synchronously, and many audio processors will call it during their audio callback. This means that not only has your handler code got to be completely thread-safe, but it's also got to be VERY fast, and avoid blocking. If you need to handle this event on your message thread, use this callback to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the message thread. */ virtual void audioProcessorChanged (AudioProcessor* processor) = 0; /** Indicates that a parameter change gesture has started. E.g. if the user is dragging a slider, this would be called when they first press the mouse button, and audioProcessorParameterChangeGestureEnd would be called when they release it. IMPORTANT NOTE: this will be called synchronously, and many audio processors will call it during their audio callback. This means that not only has your handler code got to be completely thread-safe, but it's also got to be VERY fast, and avoid blocking. If you need to handle this event on your message thread, use this callback to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the message thread. @see audioProcessorParameterChangeGestureEnd */ virtual void audioProcessorParameterChangeGestureBegin (AudioProcessor* processor, int parameterIndex); /** Indicates that a parameter change gesture has finished. E.g. if the user is dragging a slider, this would be called when they release the mouse button. IMPORTANT NOTE: this will be called synchronously, and many audio processors will call it during their audio callback. This means that not only has your handler code got to be completely thread-safe, but it's also got to be VERY fast, and avoid blocking. If you need to handle this event on your message thread, use this callback to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the message thread. @see audioProcessorParameterChangeGestureBegin */ virtual void audioProcessorParameterChangeGestureEnd (AudioProcessor* processor, int parameterIndex); }; #endif // JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED juce_AudioProcessorParameter.h000066400000000000000000000143431320201440200363060ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED #define JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED //============================================================================== /** An abstract base class for parameter objects that can be added to an AudioProcessor. @see AudioProcessor::addParameter */ class JUCE_API AudioProcessorParameter { public: AudioProcessorParameter() noexcept; /** Destructor. */ virtual ~AudioProcessorParameter(); /** Called by the host to find out the value of this parameter. Hosts will expect the value returned to be between 0 and 1.0. This could be called quite frequently, so try to make your code efficient. It's also likely to be called by non-UI threads, so the code in here should be thread-aware. */ virtual float getValue() const = 0; /** The host will call this method to change the value of one of the filter's parameters. The host may call this at any time, including during the audio processing callback, so the filter has to process this very fast and avoid blocking. If you want to set the value of a parameter internally, e.g. from your editor component, then don't call this directly - instead, use the setValueNotifyingHost() method, which will also send a message to the host telling it about the change. If the message isn't sent, the host won't be able to automate your parameters properly. The value passed will be between 0 and 1.0. */ virtual void setValue (float newValue) = 0; /** Your filter can call this when it needs to change one of its parameters. This could happen when the editor or some other internal operation changes a parameter. This method will call the setValue() method to change the value, and will then send a message to the host telling it about the change. Note that to make sure the host correctly handles automation, you should call the beginChangeGesture() and endChangeGesture() methods to tell the host when the user has started and stopped changing the parameter. */ void setValueNotifyingHost (float newValue); /** Sends a signal to the host to tell it that the user is about to start changing this parameter. This allows the host to know when a parameter is actively being held by the user, and it may use this information to help it record automation. If you call this, it must be matched by a later call to endChangeGesture(). */ void beginChangeGesture(); /** Tells the host that the user has finished changing this parameter. This allows the host to know when a parameter is actively being held by the user, and it may use this information to help it record automation. A call to this method must follow a call to beginChangeGesture(). */ void endChangeGesture(); /** This should return the default value for this parameter. */ virtual float getDefaultValue() const = 0; /** Returns the name to display for this parameter, which should be made to fit within the given string length. */ virtual String getName (int maximumStringLength) const = 0; /** Some parameters may be able to return a label string for their units. For example "Hz" or "%". */ virtual String getLabel() const = 0; /** Returns the number of discrete interval steps that this parameter's range should be quantised into. If you want a continuous range of values, don't override this method, and allow the default implementation to return AudioProcessor::getDefaultNumParameterSteps(). If your parameter is boolean, then you may want to make this return 2. The value that is returned may or may not be used, depending on the host. */ virtual int getNumSteps() const; /** Returns a textual version of the supplied parameter value. The default implementation just returns the floating point value as a string, but this could do anything you need for a custom type of value. */ virtual String getText (float value, int /*maximumStringLength*/) const; /** Should parse a string and return the appropriate value for it. */ virtual float getValueForText (const String& text) const = 0; /** This can be overridden to tell the host that this parameter operates in the reverse direction. (Not all plugin formats or hosts will actually use this information). */ virtual bool isOrientationInverted() const; /** Returns true if the host can automate this parameter. By default, this returns true. */ virtual bool isAutomatable() const; /** Should return true if this parameter is a "meta" parameter. A meta-parameter is a parameter that changes other params. It is used by some hosts (e.g. AudioUnit hosts). By default this returns false. */ virtual bool isMetaParameter() const; /** Returns the index of this parameter in its parent processor's parameter list. */ int getParameterIndex() const noexcept { return parameterIndex; } private: friend class AudioProcessor; AudioProcessor* processor; int parameterIndex; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter) }; #endif // JUCE_AUDIOPROCESSORPARAMETER_H_INCLUDED juce_GenericAudioProcessorEditor.cpp000066400000000000000000000116621320201440200374450ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class ProcessorParameterPropertyComp : public PropertyComponent, private AudioProcessorListener, private Timer { public: ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, int paramIndex) : PropertyComponent (name), owner (p), index (paramIndex), paramHasChanged (false), slider (p, paramIndex) { startTimer (100); addAndMakeVisible (slider); owner.addListener (this); } ~ProcessorParameterPropertyComp() { owner.removeListener (this); } void refresh() override { paramHasChanged = false; if (slider.getThumbBeingDragged() < 0) slider.setValue (owner.getParameter (index), dontSendNotification); slider.updateText(); } void audioProcessorChanged (AudioProcessor*) override {} void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) override { if (parameterIndex == index) paramHasChanged = true; } void timerCallback() override { if (paramHasChanged) { refresh(); startTimerHz (50); } else { startTimer (jmin (1000 / 4, getTimerInterval() + 10)); } } private: //============================================================================== class ParamSlider : public Slider { public: ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex) { const int steps = owner.getParameterNumSteps (index); if (steps > 1 && steps < 0x7fffffff) setRange (0.0, 1.0, 1.0 / (steps - 1.0)); else setRange (0.0, 1.0); setSliderStyle (Slider::LinearBar); setTextBoxIsEditable (false); setScrollWheelEnabled (true); } void valueChanged() override { const float newVal = (float) getValue(); if (owner.getParameter (index) != newVal) { owner.setParameterNotifyingHost (index, newVal); updateText(); } } String getTextFromValue (double /*value*/) override { return owner.getParameterText (index) + " " + owner.getParameterLabel (index).trimEnd(); } private: //============================================================================== AudioProcessor& owner; const int index; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamSlider) }; AudioProcessor& owner; const int index; bool volatile paramHasChanged; ParamSlider slider; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorParameterPropertyComp) }; //============================================================================== GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p) : AudioProcessorEditor (p) { jassert (p != nullptr); setOpaque (true); addAndMakeVisible (panel); Array params; const int numParams = p->getNumParameters(); int totalHeight = 0; for (int i = 0; i < numParams; ++i) { String name (p->getParameterName (i)); if (name.trim().isEmpty()) name = "Unnamed"; ProcessorParameterPropertyComp* const pc = new ProcessorParameterPropertyComp (name, *p, i); params.add (pc); totalHeight += pc->getPreferredHeight(); } panel.addProperties (params); setSize (400, jlimit (25, 400, totalHeight)); } GenericAudioProcessorEditor::~GenericAudioProcessorEditor() { } void GenericAudioProcessorEditor::paint (Graphics& g) { g.fillAll (Colours::white); } void GenericAudioProcessorEditor::resized() { panel.setBounds (getLocalBounds()); } juce_GenericAudioProcessorEditor.h000066400000000000000000000040411320201440200371030ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED #define JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED //============================================================================== /** A type of UI component that displays the parameters of an AudioProcessor as a simple list of sliders. This can be used for showing an editor for a processor that doesn't supply its own custom editor. @see AudioProcessor */ class JUCE_API GenericAudioProcessorEditor : public AudioProcessorEditor { public: //============================================================================== GenericAudioProcessorEditor (AudioProcessor* owner); ~GenericAudioProcessorEditor(); //============================================================================== void paint (Graphics&) override; void resized() override; private: //============================================================================== PropertyPanel panel; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor) }; #endif // JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED juce_PluginDescription.cpp000066400000000000000000000117631320201440200355040ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ PluginDescription::PluginDescription() : uid (0), isInstrument (false), numInputChannels (0), numOutputChannels (0), hasSharedContainer (false) { } PluginDescription::~PluginDescription() { } PluginDescription::PluginDescription (const PluginDescription& other) : name (other.name), descriptiveName (other.descriptiveName), pluginFormatName (other.pluginFormatName), category (other.category), manufacturerName (other.manufacturerName), version (other.version), fileOrIdentifier (other.fileOrIdentifier), lastFileModTime (other.lastFileModTime), uid (other.uid), isInstrument (other.isInstrument), numInputChannels (other.numInputChannels), numOutputChannels (other.numOutputChannels), hasSharedContainer (other.hasSharedContainer) { } PluginDescription& PluginDescription::operator= (const PluginDescription& other) { name = other.name; descriptiveName = other.descriptiveName; pluginFormatName = other.pluginFormatName; category = other.category; manufacturerName = other.manufacturerName; version = other.version; fileOrIdentifier = other.fileOrIdentifier; uid = other.uid; isInstrument = other.isInstrument; lastFileModTime = other.lastFileModTime; numInputChannels = other.numInputChannels; numOutputChannels = other.numOutputChannels; hasSharedContainer = other.hasSharedContainer; return *this; } bool PluginDescription::isDuplicateOf (const PluginDescription& other) const noexcept { return fileOrIdentifier == other.fileOrIdentifier && uid == other.uid; } static String getPluginDescSuffix (const PluginDescription& d) { return "-" + String::toHexString (d.fileOrIdentifier.hashCode()) + "-" + String::toHexString (d.uid); } bool PluginDescription::matchesIdentifierString (const String& identifierString) const { return identifierString.endsWithIgnoreCase (getPluginDescSuffix (*this)); } String PluginDescription::createIdentifierString() const { return pluginFormatName + "-" + name + getPluginDescSuffix (*this); } XmlElement* PluginDescription::createXml() const { XmlElement* const e = new XmlElement ("PLUGIN"); e->setAttribute ("name", name); if (descriptiveName != name) e->setAttribute ("descriptiveName", descriptiveName); e->setAttribute ("format", pluginFormatName); e->setAttribute ("category", category); e->setAttribute ("manufacturer", manufacturerName); e->setAttribute ("version", version); e->setAttribute ("file", fileOrIdentifier); e->setAttribute ("uid", String::toHexString (uid)); e->setAttribute ("isInstrument", isInstrument); e->setAttribute ("fileTime", String::toHexString (lastFileModTime.toMilliseconds())); e->setAttribute ("numInputs", numInputChannels); e->setAttribute ("numOutputs", numOutputChannels); e->setAttribute ("isShell", hasSharedContainer); return e; } bool PluginDescription::loadFromXml (const XmlElement& xml) { if (xml.hasTagName ("PLUGIN")) { name = xml.getStringAttribute ("name"); descriptiveName = xml.getStringAttribute ("descriptiveName", name); pluginFormatName = xml.getStringAttribute ("format"); category = xml.getStringAttribute ("category"); manufacturerName = xml.getStringAttribute ("manufacturer"); version = xml.getStringAttribute ("version"); fileOrIdentifier = xml.getStringAttribute ("file"); uid = xml.getStringAttribute ("uid").getHexValue32(); isInstrument = xml.getBoolAttribute ("isInstrument", false); lastFileModTime = Time (xml.getStringAttribute ("fileTime").getHexValue64()); numInputChannels = xml.getIntAttribute ("numInputs"); numOutputChannels = xml.getIntAttribute ("numOutputs"); hasSharedContainer = xml.getBoolAttribute ("isShell", false); return true; } return false; } juce_PluginDescription.h000066400000000000000000000121321320201440200351400ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/processors/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PLUGINDESCRIPTION_H_INCLUDED #define JUCE_PLUGINDESCRIPTION_H_INCLUDED //============================================================================== /** A small class to represent some facts about a particular type of plug-in. This class is for storing and managing the details about a plug-in without actually having to load an instance of it. A KnownPluginList contains a list of PluginDescription objects. @see KnownPluginList */ class JUCE_API PluginDescription { public: //============================================================================== PluginDescription(); PluginDescription (const PluginDescription& other); PluginDescription& operator= (const PluginDescription& other); ~PluginDescription(); //============================================================================== /** The name of the plug-in. */ String name; /** A more descriptive name for the plug-in. This may be the same as the 'name' field, but some plug-ins may provide an alternative name. */ String descriptiveName; /** The plug-in format, e.g. "VST", "AudioUnit", etc. */ String pluginFormatName; /** A category, such as "Dynamics", "Reverbs", etc. */ String category; /** The manufacturer. */ String manufacturerName; /** The version. This string doesn't have any particular format. */ String version; /** Either the file containing the plug-in module, or some other unique way of identifying it. E.g. for an AU, this would be an ID string that the component manager could use to retrieve the plug-in. For a VST, it's the file path. */ String fileOrIdentifier; /** The last time the plug-in file was changed. This is handy when scanning for new or changed plug-ins. */ Time lastFileModTime; /** A unique ID for the plug-in. Note that this might not be unique between formats, e.g. a VST and some other format might actually have the same id. @see createIdentifierString */ int uid; /** True if the plug-in identifies itself as a synthesiser. */ bool isInstrument; /** The number of inputs. */ int numInputChannels; /** The number of outputs. */ int numOutputChannels; /** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */ bool hasSharedContainer; /** Returns true if the two descriptions refer to the same plug-in. This isn't quite as simple as them just having the same file (because of shell plug-ins). */ bool isDuplicateOf (const PluginDescription& other) const noexcept; /** Return true if this description is equivalent to another one which created the given identifier string. Note that this isn't quite as simple as them just calling createIdentifierString() and comparing the strings, because the identifers can differ (thanks to shell plug-ins). */ bool matchesIdentifierString (const String& identifierString) const; //============================================================================== /** Returns a string that can be saved and used to uniquely identify the plugin again. This contains less info than the XML encoding, and is independent of the plug-in's file location, so can be used to store a plug-in ID for use across different machines. */ String createIdentifierString() const; //============================================================================== /** Creates an XML object containing these details. @see loadFromXml */ XmlElement* createXml() const; /** Reloads the info in this structure from an XML record that was previously saved with createXML(). Returns true if the XML was a valid plug-in description. */ bool loadFromXml (const XmlElement& xml); private: //============================================================================== JUCE_LEAK_DETECTOR (PluginDescription) }; #endif // JUCE_PLUGINDESCRIPTION_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/scanning/000077500000000000000000000000001320201440200277755ustar00rootroot00000000000000juce_KnownPluginList.cpp000066400000000000000000000415751320201440200345530ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/scanning/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ KnownPluginList::KnownPluginList() {} KnownPluginList::~KnownPluginList() {} void KnownPluginList::clear() { if (types.size() > 0) { types.clear(); sendChangeMessage(); } } PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifier) const { for (int i = 0; i < types.size(); ++i) if (types.getUnchecked(i)->fileOrIdentifier == fileOrIdentifier) return types.getUnchecked(i); return nullptr; } PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const { for (int i = 0; i < types.size(); ++i) if (types.getUnchecked(i)->matchesIdentifierString (identifierString)) return types.getUnchecked(i); return nullptr; } bool KnownPluginList::addType (const PluginDescription& type) { for (int i = types.size(); --i >= 0;) { if (types.getUnchecked(i)->isDuplicateOf (type)) { // strange - found a duplicate plugin with different info.. jassert (types.getUnchecked(i)->name == type.name); jassert (types.getUnchecked(i)->isInstrument == type.isInstrument); *types.getUnchecked(i) = type; return false; } } types.insert (0, new PluginDescription (type)); sendChangeMessage(); return true; } void KnownPluginList::removeType (const int index) { types.remove (index); sendChangeMessage(); } bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier, AudioPluginFormat& formatToUse) const { if (getTypeForFile (fileOrIdentifier) == nullptr) return false; for (int i = types.size(); --i >= 0;) { const PluginDescription* const d = types.getUnchecked(i); if (d->fileOrIdentifier == fileOrIdentifier && formatToUse.pluginNeedsRescanning (*d)) return false; } return true; } void KnownPluginList::setCustomScanner (CustomScanner* newScanner) { scanner = newScanner; } bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, const bool dontRescanIfAlreadyInList, OwnedArray & typesFound, AudioPluginFormat& format) { const ScopedLock sl (scanLock); if (dontRescanIfAlreadyInList && getTypeForFile (fileOrIdentifier) != nullptr) { bool needsRescanning = false; for (int i = types.size(); --i >= 0;) { const PluginDescription* const d = types.getUnchecked(i); if (d->fileOrIdentifier == fileOrIdentifier && d->pluginFormatName == format.getName()) { if (format.pluginNeedsRescanning (*d)) needsRescanning = true; else typesFound.add (new PluginDescription (*d)); } } if (! needsRescanning) return false; } if (blacklist.contains (fileOrIdentifier)) return false; OwnedArray found; { const ScopedUnlock sl2 (scanLock); if (scanner != nullptr) { if (! scanner->findPluginTypesFor (format, found, fileOrIdentifier)) addToBlacklist (fileOrIdentifier); } else { format.findAllTypesForFile (found, fileOrIdentifier); } } for (int i = 0; i < found.size(); ++i) { PluginDescription* const desc = found.getUnchecked(i); jassert (desc != nullptr); addType (*desc); typesFound.add (new PluginDescription (*desc)); } return found.size() > 0; } void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager, const StringArray& files, OwnedArray & typesFound) { for (int i = 0; i < files.size(); ++i) { const String filenameOrID (files[i]); bool found = false; for (int j = 0; j < formatManager.getNumFormats(); ++j) { AudioPluginFormat* const format = formatManager.getFormat (j); if (format->fileMightContainThisPluginType (filenameOrID) && scanAndAddFile (filenameOrID, true, typesFound, *format)) { found = true; break; } } if (! found) { const File f (filenameOrID); if (f.isDirectory()) { StringArray s; { Array subFiles; f.findChildFiles (subFiles, File::findFilesAndDirectories, false); for (int j = 0; j < subFiles.size(); ++j) s.add (subFiles.getReference(j).getFullPathName()); } scanAndAddDragAndDroppedFiles (formatManager, s, typesFound); } } } scanFinished(); } void KnownPluginList::scanFinished() { if (scanner != nullptr) scanner->scanFinished(); } const StringArray& KnownPluginList::getBlacklistedFiles() const { return blacklist; } void KnownPluginList::addToBlacklist (const String& pluginID) { if (! blacklist.contains (pluginID)) { blacklist.add (pluginID); sendChangeMessage(); } } void KnownPluginList::removeFromBlacklist (const String& pluginID) { const int index = blacklist.indexOf (pluginID); if (index >= 0) { blacklist.remove (index); sendChangeMessage(); } } void KnownPluginList::clearBlacklistedFiles() { if (blacklist.size() > 0) { blacklist.clear(); sendChangeMessage(); } } //============================================================================== struct PluginSorter { PluginSorter (KnownPluginList::SortMethod sortMethod, bool forwards) noexcept : method (sortMethod), direction (forwards ? 1 : -1) {} int compareElements (const PluginDescription* const first, const PluginDescription* const second) const { int diff = 0; switch (method) { case KnownPluginList::sortByCategory: diff = first->category.compareNatural (second->category); break; case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareNatural (second->manufacturerName); break; case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break; case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break; default: break; } if (diff == 0) diff = first->name.compareNatural (second->name); return diff * direction; } private: static String lastPathPart (const String& path) { return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false); } const KnownPluginList::SortMethod method; const int direction; JUCE_DECLARE_NON_COPYABLE (PluginSorter) }; void KnownPluginList::sort (const SortMethod method, bool forwards) { if (method != defaultOrder) { Array oldOrder, newOrder; oldOrder.addArray (types); PluginSorter sorter (method, forwards); types.sort (sorter, true); newOrder.addArray (types); if (oldOrder != newOrder) sendChangeMessage(); } } //============================================================================== XmlElement* KnownPluginList::createXml() const { XmlElement* const e = new XmlElement ("KNOWNPLUGINS"); for (int i = types.size(); --i >= 0;) e->prependChildElement (types.getUnchecked(i)->createXml()); for (int i = 0; i < blacklist.size(); ++i) e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", blacklist[i]); return e; } void KnownPluginList::recreateFromXml (const XmlElement& xml) { clear(); clearBlacklistedFiles(); if (xml.hasTagName ("KNOWNPLUGINS")) { forEachXmlChildElement (xml, e) { PluginDescription info; if (e->hasTagName ("BLACKLISTED")) blacklist.add (e->getStringAttribute ("id")); else if (info.loadFromXml (*e)) addType (info); } } } //============================================================================== struct PluginTreeUtils { enum { menuIdBase = 0x324503f4 }; static void buildTreeByFolder (KnownPluginList::PluginTree& tree, const Array & allPlugins) { for (int i = 0; i < allPlugins.size(); ++i) { PluginDescription* const pd = allPlugins.getUnchecked (i); String path (pd->fileOrIdentifier.replaceCharacter ('\\', '/') .upToLastOccurrenceOf ("/", false, false)); if (path.substring (1, 2) == ":") path = path.substring (2); addPlugin (tree, pd, path); } optimiseFolders (tree, false); } static void optimiseFolders (KnownPluginList::PluginTree& tree, bool concatenateName) { for (int i = tree.subFolders.size(); --i >= 0;) { KnownPluginList::PluginTree& sub = *tree.subFolders.getUnchecked(i); optimiseFolders (sub, concatenateName || (tree.subFolders.size() > 1)); if (sub.plugins.size() == 0) { for (int j = 0; j < sub.subFolders.size(); ++j) { KnownPluginList::PluginTree* const s = sub.subFolders.getUnchecked(j); if (concatenateName) s->folder = sub.folder + "/" + s->folder; tree.subFolders.add (s); } sub.subFolders.clear (false); tree.subFolders.remove (i); } } } static void buildTreeByCategory (KnownPluginList::PluginTree& tree, const Array & sorted, const KnownPluginList::SortMethod sortMethod) { String lastType; ScopedPointer current (new KnownPluginList::PluginTree()); for (int i = 0; i < sorted.size(); ++i) { const PluginDescription* const pd = sorted.getUnchecked(i); String thisType (sortMethod == KnownPluginList::sortByCategory ? pd->category : pd->manufacturerName); if (! thisType.containsNonWhitespaceChars()) thisType = "Other"; if (thisType != lastType) { if (current->plugins.size() + current->subFolders.size() > 0) { current->folder = lastType; tree.subFolders.add (current.release()); current = new KnownPluginList::PluginTree(); } lastType = thisType; } current->plugins.add (pd); } if (current->plugins.size() + current->subFolders.size() > 0) { current->folder = lastType; tree.subFolders.add (current.release()); } } static void addPlugin (KnownPluginList::PluginTree& tree, PluginDescription* const pd, String path) { if (path.isEmpty()) { tree.plugins.add (pd); } else { #if JUCE_MAC if (path.containsChar (':')) path = path.fromFirstOccurrenceOf (":", false, false); // avoid the special AU formatting nonsense on Mac.. #endif const String firstSubFolder (path.upToFirstOccurrenceOf ("/", false, false)); const String remainingPath (path.fromFirstOccurrenceOf ("/", false, false)); for (int i = tree.subFolders.size(); --i >= 0;) { KnownPluginList::PluginTree& subFolder = *tree.subFolders.getUnchecked(i); if (subFolder.folder.equalsIgnoreCase (firstSubFolder)) { addPlugin (subFolder, pd, remainingPath); return; } } KnownPluginList::PluginTree* const newFolder = new KnownPluginList::PluginTree(); newFolder->folder = firstSubFolder; tree.subFolders.add (newFolder); addPlugin (*newFolder, pd, remainingPath); } } static bool containsDuplicateNames (const Array& plugins, const String& name) { int matches = 0; for (int i = 0; i < plugins.size(); ++i) if (plugins.getUnchecked(i)->name == name) if (++matches > 1) return true; return false; } static void addToMenu (const KnownPluginList::PluginTree& tree, PopupMenu& m, const OwnedArray & allPlugins) { for (int i = 0; i < tree.subFolders.size(); ++i) { const KnownPluginList::PluginTree& sub = *tree.subFolders.getUnchecked(i); PopupMenu subMenu; addToMenu (sub, subMenu, allPlugins); m.addSubMenu (sub.folder, subMenu); } for (int i = 0; i < tree.plugins.size(); ++i) { const PluginDescription* const plugin = tree.plugins.getUnchecked(i); String name (plugin->name); if (containsDuplicateNames (tree.plugins, name)) name << " (" << plugin->pluginFormatName << ')'; m.addItem (allPlugins.indexOf (plugin) + menuIdBase, name, true, false); } } }; KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortMethod) const { Array sorted; { PluginSorter sorter (sortMethod, true); for (int i = 0; i < types.size(); ++i) sorted.addSorted (sorter, types.getUnchecked(i)); } PluginTree* tree = new PluginTree(); if (sortMethod == sortByCategory || sortMethod == sortByManufacturer || sortMethod == sortByFormat) { PluginTreeUtils::buildTreeByCategory (*tree, sorted, sortMethod); } else if (sortMethod == sortByFileSystemLocation) { PluginTreeUtils::buildTreeByFolder (*tree, sorted); } else { for (int i = 0; i < sorted.size(); ++i) tree->plugins.add (sorted.getUnchecked(i)); } return tree; } //============================================================================== void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const { ScopedPointer tree (createTree (sortMethod)); PluginTreeUtils::addToMenu (*tree, menu, types); } int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const { const int i = menuResultCode - PluginTreeUtils::menuIdBase; return isPositiveAndBelow (i, types.size()) ? i : -1; } //============================================================================== KnownPluginList::CustomScanner::CustomScanner() {} KnownPluginList::CustomScanner::~CustomScanner() {} void KnownPluginList::CustomScanner::scanFinished() {} bool KnownPluginList::CustomScanner::shouldExit() const noexcept { if (ThreadPoolJob* job = ThreadPoolJob::getCurrentThreadPoolJob()) return job->shouldExit(); return false; } juce_KnownPluginList.h000066400000000000000000000207721320201440200342140ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/scanning/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_KNOWNPLUGINLIST_H_INCLUDED #define JUCE_KNOWNPLUGINLIST_H_INCLUDED //============================================================================== /** Manages a list of plugin types. This can be easily edited, saved and loaded, and used to create instances of the plugin types in it. @see PluginListComponent */ class JUCE_API KnownPluginList : public ChangeBroadcaster { public: //============================================================================== /** Creates an empty list. */ KnownPluginList(); /** Destructor. */ ~KnownPluginList(); //============================================================================== /** Clears the list. */ void clear(); /** Returns the number of types currently in the list. @see getType */ int getNumTypes() const noexcept { return types.size(); } /** Returns one of the types. @see getNumTypes */ PluginDescription* getType (int index) const noexcept { return types [index]; } /** Type iteration. */ PluginDescription** begin() const noexcept { return types.begin(); } /** Type iteration. */ PluginDescription** end() const noexcept { return types.end(); } /** Looks for a type in the list which comes from this file. */ PluginDescription* getTypeForFile (const String& fileOrIdentifier) const; /** Looks for a type in the list which matches a plugin type ID. The identifierString parameter must have been created by PluginDescription::createIdentifierString(). */ PluginDescription* getTypeForIdentifierString (const String& identifierString) const; /** Adds a type manually from its description. */ bool addType (const PluginDescription& type); /** Removes a type. */ void removeType (int index); /** Looks for all types that can be loaded from a given file, and adds them to the list. If dontRescanIfAlreadyInList is true, then the file will only be loaded and re-tested if it's not already in the list, or if the file's modification time has changed since the list was created. If dontRescanIfAlreadyInList is false, the file will always be reloaded and tested. Returns true if any new types were added, and all the types found in this file (even if it was already known and hasn't been re-scanned) get returned in the array. */ bool scanAndAddFile (const String& possiblePluginFileOrIdentifier, bool dontRescanIfAlreadyInList, OwnedArray & typesFound, AudioPluginFormat& formatToUse); /** Tells a custom scanner that a scan has finished, and it can release any resources. */ void scanFinished(); /** Returns true if the specified file is already known about and if it hasn't been modified since our entry was created. */ bool isListingUpToDate (const String& possiblePluginFileOrIdentifier, AudioPluginFormat& formatToUse) const; /** Scans and adds a bunch of files that might have been dragged-and-dropped. If any types are found in the files, their descriptions are returned in the array. */ void scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager, const StringArray& filenames, OwnedArray & typesFound); //============================================================================== /** Returns the list of blacklisted files. */ const StringArray& getBlacklistedFiles() const; /** Adds a plugin ID to the black-list. */ void addToBlacklist (const String& pluginID); /** Removes a plugin ID from the black-list. */ void removeFromBlacklist (const String& pluginID); /** Clears all the blacklisted files. */ void clearBlacklistedFiles(); //============================================================================== /** Sort methods used to change the order of the plugins in the list. */ enum SortMethod { defaultOrder = 0, sortAlphabetically, sortByCategory, sortByManufacturer, sortByFormat, sortByFileSystemLocation }; //============================================================================== /** Adds all the plugin types to a popup menu so that the user can select one. Depending on the sort method, it may add sub-menus for categories, manufacturers, etc. Use getIndexChosenByMenu() to find out the type that was chosen. */ void addToMenu (PopupMenu& menu, SortMethod sortMethod) const; /** Converts a menu item index that has been chosen into its index in this list. Returns -1 if it's not an ID that was used. @see addToMenu */ int getIndexChosenByMenu (int menuResultCode) const; //============================================================================== /** Sorts the list. */ void sort (SortMethod method, bool forwards); //============================================================================== /** Creates some XML that can be used to store the state of this list. */ XmlElement* createXml() const; /** Recreates the state of this list from its stored XML format. */ void recreateFromXml (const XmlElement& xml); //============================================================================== /** A structure that recursively holds a tree of plugins. @see KnownPluginList::createTree() */ struct PluginTree { String folder; /**< The name of this folder in the tree */ OwnedArray subFolders; Array plugins; }; /** Creates a PluginTree object containing all the known plugins. */ PluginTree* createTree (const SortMethod sortMethod) const; //============================================================================== class CustomScanner { public: CustomScanner(); virtual ~CustomScanner(); /** Attempts to load the given file and find a list of plugins in it. @returns true if the plugin loaded, false if it crashed */ virtual bool findPluginTypesFor (AudioPluginFormat& format, OwnedArray & result, const String& fileOrIdentifier) = 0; /** Called when a scan has finished, to allow clean-up of resources. */ virtual void scanFinished(); /** Returns true if the current scan should be abandoned. Any blocking methods should check this value repeatedly and return if if becomes true. */ bool shouldExit() const noexcept; }; /** Supplies a custom scanner to be used in future scans. The KnownPluginList will take ownership of the object passed in. */ void setCustomScanner (CustomScanner*); private: //============================================================================== OwnedArray types; StringArray blacklist; ScopedPointer scanner; CriticalSection scanLock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList) }; #endif // JUCE_KNOWNPLUGINLIST_H_INCLUDED juce_PluginDirectoryScanner.cpp000066400000000000000000000116651320201440200360760ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/scanning/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ static StringArray readDeadMansPedalFile (const File& file) { StringArray lines; file.readLines (lines); lines.removeEmptyStrings(); return lines; } PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, AudioPluginFormat& formatToLookFor, FileSearchPath directoriesToSearch, const bool recursive, const File& deadMansPedal) : list (listToAddTo), format (formatToLookFor), deadMansPedalFile (deadMansPedal), progress (0) { directoriesToSearch.removeRedundantPaths(); filesOrIdentifiersToScan = format.searchPathsForPlugins (directoriesToSearch, recursive); // If any plugins have crashed recently when being loaded, move them to the // end of the list to give the others a chance to load correctly.. const StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile)); for (int i = 0; i < crashedPlugins.size(); ++i) { const String f = crashedPlugins[i]; for (int j = filesOrIdentifiersToScan.size(); --j >= 0;) if (f == filesOrIdentifiersToScan[j]) filesOrIdentifiersToScan.move (j, -1); } applyBlacklistingsFromDeadMansPedal (listToAddTo, deadMansPedalFile); nextIndex.set (filesOrIdentifiersToScan.size()); } PluginDirectoryScanner::~PluginDirectoryScanner() { list.scanFinished(); } //============================================================================== String PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const { return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex.get() - 1]); } void PluginDirectoryScanner::updateProgress() { progress = (1.0f - nextIndex.get() / (float) filesOrIdentifiersToScan.size()); } bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList, String& nameOfPluginBeingScanned) { const int index = --nextIndex; if (index >= 0) { const String file (filesOrIdentifiersToScan [index]); if (file.isNotEmpty() && ! list.isListingUpToDate (file, format)) { nameOfPluginBeingScanned = format.getNameOfPluginFromIdentifier (file); OwnedArray typesFound; // Add this plugin to the end of the dead-man's pedal list in case it crashes... StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile)); crashedPlugins.removeString (file); crashedPlugins.add (file); setDeadMansPedalFile (crashedPlugins); list.scanAndAddFile (file, dontRescanIfAlreadyInList, typesFound, format); // Managed to load without crashing, so remove it from the dead-man's-pedal.. crashedPlugins.removeString (file); setDeadMansPedalFile (crashedPlugins); if (typesFound.size() == 0 && ! list.getBlacklistedFiles().contains (file)) failedFiles.add (file); } } updateProgress(); return index > 0; } bool PluginDirectoryScanner::skipNextFile() { updateProgress(); return --nextIndex > 0; } void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents) { if (deadMansPedalFile.getFullPathName().isNotEmpty()) deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true); } void PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (KnownPluginList& list, const File& file) { // If any plugins have crashed recently when being loaded, move them to the // end of the list to give the others a chance to load correctly.. const StringArray crashedPlugins (readDeadMansPedalFile (file)); for (int i = 0; i < crashedPlugins.size(); ++i) list.addToBlacklist (crashedPlugins[i]); } juce_PluginDirectoryScanner.h000066400000000000000000000130711320201440200355340ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/scanning/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED #define JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED //============================================================================== /** Scans a directory for plugins, and adds them to a KnownPluginList. To use one of these, create it and call scanNextFile() repeatedly, until it returns false. */ class JUCE_API PluginDirectoryScanner { public: //============================================================================== /** Creates a scanner. @param listToAddResultsTo this will get the new types added to it. @param formatToLookFor this is the type of format that you want to look for @param directoriesToSearch the path to search @param searchRecursively true to search recursively @param deadMansPedalFile if this isn't File::nonexistent, then it will be used as a file to store the names of any plugins that crash during initialisation. If there are any plugins listed in it, then these will always be scanned after all other possible files have been tried - in this way, even if there's a few dodgy plugins in your path, then a couple of rescans will still manage to find all the proper plugins. It's probably best to choose a file in the user's application data directory (alongside your app's settings file) for this. The file format it uses is just a list of filenames of the modules that failed. */ PluginDirectoryScanner (KnownPluginList& listToAddResultsTo, AudioPluginFormat& formatToLookFor, FileSearchPath directoriesToSearch, bool searchRecursively, const File& deadMansPedalFile); /** Destructor. */ ~PluginDirectoryScanner(); //============================================================================== /** Tries the next likely-looking file. If dontRescanIfAlreadyInList is true, then the file will only be loaded and re-tested if it's not already in the list, or if the file's modification time has changed since the list was created. If dontRescanIfAlreadyInList is false, the file will always be reloaded and tested. The nameOfPluginBeingScanned will be updated to the name of the plugin being scanned before the scan starts. Returns false when there are no more files to try. */ bool scanNextFile (bool dontRescanIfAlreadyInList, String& nameOfPluginBeingScanned); /** Skips over the next file without scanning it. Returns false when there are no more files to try. */ bool skipNextFile(); /** Returns the description of the plugin that will be scanned during the next call to scanNextFile(). This is handy if you want to show the user which file is currently getting scanned. */ String getNextPluginFileThatWillBeScanned() const; /** Returns the estimated progress, between 0 and 1. */ float getProgress() const { return progress; } /** This returns a list of all the filenames of things that looked like being a plugin file, but which failed to open for some reason. */ const StringArray& getFailedFiles() const noexcept { return failedFiles; } /** Reads the given dead-mans-pedal file and applies its contents to the list. */ static void applyBlacklistingsFromDeadMansPedal (KnownPluginList& listToApplyTo, const File& deadMansPedalFile); private: //============================================================================== KnownPluginList& list; AudioPluginFormat& format; StringArray filesOrIdentifiersToScan; File deadMansPedalFile; StringArray failedFiles; Atomic nextIndex; float progress; void updateProgress(); void setDeadMansPedalFile (const StringArray& newContents); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner) }; #endif // JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED juce_PluginListComponent.cpp000066400000000000000000000465141320201440200354170ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/scanning/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class PluginListComponent::TableModel : public TableListBoxModel { public: TableModel (PluginListComponent& c, KnownPluginList& l) : owner (c), list (l) {} int getNumRows() override { return list.getNumTypes() + list.getBlacklistedFiles().size(); } void paintRowBackground (Graphics& g, int /*rowNumber*/, int /*width*/, int /*height*/, bool rowIsSelected) override { if (rowIsSelected) g.fillAll (owner.findColour (TextEditor::highlightColourId)); } enum { nameCol = 1, typeCol = 2, categoryCol = 3, manufacturerCol = 4, descCol = 5 }; void paintCell (Graphics& g, int row, int columnId, int width, int height, bool /*rowIsSelected*/) override { String text; bool isBlacklisted = row >= list.getNumTypes(); if (isBlacklisted) { if (columnId == nameCol) text = list.getBlacklistedFiles() [row - list.getNumTypes()]; else if (columnId == descCol) text = TRANS("Deactivated after failing to initialise correctly"); } else if (const PluginDescription* const desc = list.getType (row)) { switch (columnId) { case nameCol: text = desc->name; break; case typeCol: text = desc->pluginFormatName; break; case categoryCol: text = desc->category.isNotEmpty() ? desc->category : "-"; break; case manufacturerCol: text = desc->manufacturerName; break; case descCol: text = getPluginDescription (*desc); break; default: jassertfalse; break; } } if (text.isNotEmpty()) { g.setColour (isBlacklisted ? Colours::red : columnId == nameCol ? Colours::black : Colours::grey); g.setFont (Font (height * 0.7f, Font::bold)); g.drawFittedText (text, 4, 0, width - 6, height, Justification::centredLeft, 1, 0.9f); } } void deleteKeyPressed (int) override { owner.removeSelectedPlugins(); } void sortOrderChanged (int newSortColumnId, bool isForwards) override { switch (newSortColumnId) { case nameCol: list.sort (KnownPluginList::sortAlphabetically, isForwards); break; case typeCol: list.sort (KnownPluginList::sortByFormat, isForwards); break; case categoryCol: list.sort (KnownPluginList::sortByCategory, isForwards); break; case manufacturerCol: list.sort (KnownPluginList::sortByManufacturer, isForwards); break; case descCol: break; default: jassertfalse; break; } } static String getPluginDescription (const PluginDescription& desc) { StringArray items; if (desc.descriptiveName != desc.name) items.add (desc.descriptiveName); items.add (desc.version); items.removeEmptyStrings(); return items.joinIntoString (" - "); } PluginListComponent& owner; KnownPluginList& list; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableModel) }; //============================================================================== PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit, const File& deadMansPedal, PropertiesFile* const props) : formatManager (manager), list (listToEdit), deadMansPedalFile (deadMansPedal), optionsButton ("Options..."), propertiesToUse (props), numThreads (0) { tableModel = new TableModel (*this, listToEdit); TableHeaderComponent& header = table.getHeader(); header.addColumn (TRANS("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); header.addColumn (TRANS("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); header.addColumn (TRANS("Category"), TableModel::categoryCol, 100, 100, 200); header.addColumn (TRANS("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); header.addColumn (TRANS("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); table.setHeaderHeight (22); table.setRowHeight (20); table.setModel (tableModel); table.setMultipleSelectionEnabled (true); addAndMakeVisible (table); addAndMakeVisible (optionsButton); optionsButton.addListener (this); optionsButton.setTriggeredOnMouseDown (true); setSize (400, 600); list.addChangeListener (this); updateList(); table.getHeader().reSortTable(); PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (list, deadMansPedalFile); deadMansPedalFile.deleteFile(); } PluginListComponent::~PluginListComponent() { list.removeChangeListener (this); } void PluginListComponent::setOptionsButtonText (const String& newText) { optionsButton.setButtonText (newText); resized(); } void PluginListComponent::setScanDialogText (const String& title, const String& content) { dialogTitle = title; dialogText = content; } void PluginListComponent::setNumberOfThreadsForScanning (int num) { numThreads = num; } void PluginListComponent::resized() { Rectangle r (getLocalBounds().reduced (2)); optionsButton.setBounds (r.removeFromBottom (24)); optionsButton.changeWidthToFitText (24); r.removeFromBottom (3); table.setBounds (r); } void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) { table.getHeader().reSortTable(); updateList(); } void PluginListComponent::updateList() { table.updateContent(); table.repaint(); } void PluginListComponent::removeSelectedPlugins() { const SparseSet selected (table.getSelectedRows()); for (int i = table.getNumRows(); --i >= 0;) if (selected.contains (i)) removePluginItem (i); } void PluginListComponent::setTableModel (TableListBoxModel* model) { table.setModel (nullptr); tableModel = model; table.setModel (tableModel); table.getHeader().reSortTable(); table.updateContent(); table.repaint(); } bool PluginListComponent::canShowSelectedFolder() const { if (const PluginDescription* const desc = list.getType (table.getSelectedRow())) return File::createFileWithoutCheckingPath (desc->fileOrIdentifier).exists(); return false; } void PluginListComponent::showSelectedFolder() { if (canShowSelectedFolder()) if (const PluginDescription* const desc = list.getType (table.getSelectedRow())) File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); } void PluginListComponent::removeMissingPlugins() { for (int i = list.getNumTypes(); --i >= 0;) if (! formatManager.doesPluginStillExist (*list.getType (i))) list.removeType (i); } void PluginListComponent::removePluginItem (int index) { if (index < list.getNumTypes()) list.removeType (index); else list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); } void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList) { if (pluginList != nullptr) pluginList->optionsMenuCallback (result); } void PluginListComponent::optionsMenuCallback (int result) { switch (result) { case 0: break; case 1: list.clear(); break; case 2: removeSelectedPlugins(); break; case 3: showSelectedFolder(); break; case 4: removeMissingPlugins(); break; default: if (AudioPluginFormat* format = formatManager.getFormat (result - 10)) scanFor (*format); break; } } void PluginListComponent::buttonClicked (Button* button) { if (button == &optionsButton) { PopupMenu menu; menu.addItem (1, TRANS("Clear list")); menu.addItem (2, TRANS("Remove selected plug-in from list"), table.getNumSelectedRows() > 0); menu.addItem (3, TRANS("Show folder containing selected plug-in"), canShowSelectedFolder()); menu.addItem (4, TRANS("Remove any plug-ins whose files no longer exist")); menu.addSeparator(); for (int i = 0; i < formatManager.getNumFormats(); ++i) { AudioPluginFormat* const format = formatManager.getFormat (i); if (format->canScanForPlugins()) menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plug-ins"); } menu.showMenuAsync (PopupMenu::Options().withTargetComponent (&optionsButton), ModalCallbackFunction::forComponent (optionsMenuStaticCallback, this)); } } bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) { return true; } void PluginListComponent::filesDropped (const StringArray& files, int, int) { OwnedArray typesFound; list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); } FileSearchPath PluginListComponent::getLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format) { return FileSearchPath (properties.getValue ("lastPluginScanPath_" + format.getName(), format.getDefaultLocationsToSearch().toString())); } void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format, const FileSearchPath& newPath) { properties.setValue ("lastPluginScanPath_" + format.getName(), newPath.toString()); } //============================================================================== class PluginListComponent::Scanner : private Timer { public: Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, int threads, const String& title, const String& text) : owner (plc), formatToScan (format), propertiesToUse (properties), pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon), progressWindow (title, text, AlertWindow::NoIcon), progress (0.0), numThreads (threads), finished (false) { FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); if (path.getNumPaths() > 0) // if the path is empty, then paths aren't used for this format. { if (propertiesToUse != nullptr) path = getLastSearchPath (*propertiesToUse, formatToScan); pathList.setSize (500, 300); pathList.setPath (path); pathChooserWindow.addCustomComponent (&pathList); pathChooserWindow.addButton (TRANS("Scan"), 1, KeyPress (KeyPress::returnKey)); pathChooserWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); pathChooserWindow.enterModalState (true, ModalCallbackFunction::forComponent (startScanCallback, &pathChooserWindow, this), false); } else { startScan(); } } ~Scanner() { if (pool != nullptr) { pool->removeAllJobs (true, 60000); pool = nullptr; } } private: PluginListComponent& owner; AudioPluginFormat& formatToScan; PropertiesFile* propertiesToUse; ScopedPointer scanner; AlertWindow pathChooserWindow, progressWindow; FileSearchPathListComponent pathList; String pluginBeingScanned; double progress; int numThreads; bool finished; ScopedPointer pool; static void startScanCallback (int result, AlertWindow* alert, Scanner* scanner) { if (alert != nullptr && scanner != nullptr) { if (result != 0) scanner->warnUserAboutStupidPaths(); else scanner->finishedScan(); } } // Try to dissuade people from to scanning their entire C: drive, or other system folders. void warnUserAboutStupidPaths() { for (int i = 0; i < pathList.getPath().getNumPaths(); ++i) { const File f (pathList.getPath()[i]); if (isStupidPath (f)) { AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, TRANS("Plugin Scanning"), TRANS("If you choose to scan folders that contain non-plugin files, " "then scanning may take a long time, and can cause crashes when " "attempting to load unsuitable files.") + newLine + TRANS ("Are you sure you want to scan the folder \"XYZ\"?") .replace ("XYZ", f.getFullPathName()), TRANS ("Scan"), String::empty, nullptr, ModalCallbackFunction::create (warnAboutStupidPathsCallback, this)); return; } } startScan(); } static bool isStupidPath (const File& f) { Array roots; File::findFileSystemRoots (roots); if (roots.contains (f)) return true; File::SpecialLocationType pathsThatWouldBeStupidToScan[] = { File::globalApplicationsDirectory, File::userHomeDirectory, File::userDocumentsDirectory, File::userDesktopDirectory, File::tempDirectory, File::userMusicDirectory, File::userMoviesDirectory, File::userPicturesDirectory }; for (int i = 0; i < numElementsInArray (pathsThatWouldBeStupidToScan); ++i) { const File sillyFolder (File::getSpecialLocation (pathsThatWouldBeStupidToScan[i])); if (f == sillyFolder || sillyFolder.isAChildOf (f)) return true; } return false; } static void warnAboutStupidPathsCallback (int result, Scanner* scanner) { if (result != 0) scanner->startScan(); else scanner->finishedScan(); } void startScan() { pathChooserWindow.setVisible (false); scanner = new PluginDirectoryScanner (owner.list, formatToScan, pathList.getPath(), true, owner.deadMansPedalFile); if (propertiesToUse != nullptr) { setLastSearchPath (*propertiesToUse, formatToScan, pathList.getPath()); propertiesToUse->saveIfNeeded(); } progressWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); progressWindow.addProgressBarComponent (progress); progressWindow.enterModalState(); if (numThreads > 0) { pool = new ThreadPool (numThreads); for (int i = numThreads; --i >= 0;) pool->addJob (new ScanJob (*this), true); } startTimer (20); } void finishedScan() { owner.scanFinished (scanner != nullptr ? scanner->getFailedFiles() : StringArray()); } void timerCallback() override { if (pool == nullptr) { if (doNextScan()) startTimer (20); } if (! progressWindow.isCurrentlyModal()) finished = true; if (finished) finishedScan(); else progressWindow.setMessage (TRANS("Testing") + ":\n\n" + pluginBeingScanned); } bool doNextScan() { if (scanner->scanNextFile (true, pluginBeingScanned)) { progress = scanner->getProgress(); return true; } finished = true; return false; } struct ScanJob : public ThreadPoolJob { ScanJob (Scanner& s) : ThreadPoolJob ("pluginscan"), scanner (s) {} JobStatus runJob() { while (scanner.doNextScan() && ! shouldExit()) {} return jobHasFinished; } Scanner& scanner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScanJob) }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scanner) }; void PluginListComponent::scanFor (AudioPluginFormat& format) { currentScanner = new Scanner (*this, format, propertiesToUse, numThreads, dialogTitle.isNotEmpty() ? dialogTitle : TRANS("Scanning for plug-ins..."), dialogText.isNotEmpty() ? dialogText : TRANS("Searching for all possible plug-in files...")); } bool PluginListComponent::isScanning() const noexcept { return currentScanner != nullptr; } void PluginListComponent::scanFinished (const StringArray& failedFiles) { StringArray shortNames; for (int i = 0; i < failedFiles.size(); ++i) shortNames.add (File::createFileWithoutCheckingPath (failedFiles[i]).getFileName()); currentScanner = nullptr; // mustn't delete this before using the failed files array if (shortNames.size() > 0) AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, TRANS("Scan complete"), TRANS("Note that the following files appeared to be plugin files, but failed to load correctly") + ":\n\n" + shortNames.joinIntoString (", ")); } juce_PluginListComponent.h000066400000000000000000000117151320201440200350570ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_audio_processors/scanning/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PLUGINLISTCOMPONENT_H_INCLUDED #define JUCE_PLUGINLISTCOMPONENT_H_INCLUDED //============================================================================== /** A component displaying a list of plugins, with options to scan for them, add, remove and sort them. */ class JUCE_API PluginListComponent : public Component, public FileDragAndDropTarget, private ChangeListener, private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug) { public: //============================================================================== /** Creates the list component. For info about the deadMansPedalFile, see the PluginDirectoryScanner constructor. The properties file, if supplied, is used to store the user's last search paths. */ PluginListComponent (AudioPluginFormatManager& formatManager, KnownPluginList& listToRepresent, const File& deadMansPedalFile, PropertiesFile* propertiesToUse); /** Destructor. */ ~PluginListComponent(); /** Changes the text in the panel's options button. */ void setOptionsButtonText (const String& newText); /** Changes the text in the progress dialog box that is shown when scanning. */ void setScanDialogText (const String& textForProgressWindowTitle, const String& textForProgressWindowDescription); /** Sets how many threads to simultaneously scan for plugins. If this is 0, then all scanning happens on the message thread (this is the default) */ void setNumberOfThreadsForScanning (int numThreads); /** Returns the last search path stored in a given properties file for the specified format. */ static FileSearchPath getLastSearchPath (PropertiesFile&, AudioPluginFormat&); /** Stores a search path in a properties file for the given format. */ static void setLastSearchPath (PropertiesFile&, AudioPluginFormat&, const FileSearchPath&); /** Triggers an asynchronous scan for the given format. */ void scanFor (AudioPluginFormat&); /** Returns true if there's currently a scan in progress. */ bool isScanning() const noexcept; /** Removes the plugins currently selected in the table. */ void removeSelectedPlugins(); /** Sets a custom table model to be used. This will take ownership of the model and delete it when no longer needed. */ void setTableModel (TableListBoxModel* model); /** Returns the table used to display the plugin list. */ TableListBox& getTableListBox() noexcept { return table; } private: //============================================================================== AudioPluginFormatManager& formatManager; KnownPluginList& list; File deadMansPedalFile; TableListBox table; TextButton optionsButton; PropertiesFile* propertiesToUse; String dialogTitle, dialogText; int numThreads; class TableModel; ScopedPointer tableModel; class Scanner; friend class Scanner; friend struct ContainerDeletePolicy; ScopedPointer currentScanner; void scanFinished (const StringArray&); static void optionsMenuStaticCallback (int, PluginListComponent*); void optionsMenuCallback (int); void updateList(); void showSelectedFolder(); bool canShowSelectedFolder() const; void removeMissingPlugins(); void removePluginItem (int index); void resized() override; bool isInterestedInFileDrag (const StringArray&) override; void filesDropped (const StringArray&, int, int) override; void buttonClicked (Button*) override; void changeListenerCallback (ChangeBroadcaster*) override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListComponent) }; #endif // JUCE_PLUGINLISTCOMPONENT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/000077500000000000000000000000001320201440200235425ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/000077500000000000000000000000001320201440200257075ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp000066400000000000000000000157571320201440200320070ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ AbstractFifo::AbstractFifo (const int capacity) noexcept : bufferSize (capacity) { jassert (bufferSize > 0); } AbstractFifo::~AbstractFifo() {} int AbstractFifo::getTotalSize() const noexcept { return bufferSize; } int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; } int AbstractFifo::getNumReady() const noexcept { const int vs = validStart.get(); const int ve = validEnd.get(); return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); } void AbstractFifo::reset() noexcept { validEnd = 0; validStart = 0; } void AbstractFifo::setTotalSize (int newSize) noexcept { jassert (newSize > 0); reset(); bufferSize = newSize; } //============================================================================== void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept { const int vs = validStart.get(); const int ve = validEnd.value; const int freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve); numToWrite = jmin (numToWrite, freeSpace - 1); if (numToWrite <= 0) { startIndex1 = 0; startIndex2 = 0; blockSize1 = 0; blockSize2 = 0; } else { startIndex1 = ve; startIndex2 = 0; blockSize1 = jmin (bufferSize - ve, numToWrite); numToWrite -= blockSize1; blockSize2 = numToWrite <= 0 ? 0 : jmin (numToWrite, vs); } } void AbstractFifo::finishedWrite (int numWritten) noexcept { jassert (numWritten >= 0 && numWritten < bufferSize); int newEnd = validEnd.value + numWritten; if (newEnd >= bufferSize) newEnd -= bufferSize; validEnd = newEnd; } void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept { const int vs = validStart.value; const int ve = validEnd.get(); const int numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); numWanted = jmin (numWanted, numReady); if (numWanted <= 0) { startIndex1 = 0; startIndex2 = 0; blockSize1 = 0; blockSize2 = 0; } else { startIndex1 = vs; startIndex2 = 0; blockSize1 = jmin (bufferSize - vs, numWanted); numWanted -= blockSize1; blockSize2 = numWanted <= 0 ? 0 : jmin (numWanted, ve); } } void AbstractFifo::finishedRead (int numRead) noexcept { jassert (numRead >= 0 && numRead <= bufferSize); int newStart = validStart.value + numRead; if (newStart >= bufferSize) newStart -= bufferSize; validStart = newStart; } //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class AbstractFifoTests : public UnitTest { public: AbstractFifoTests() : UnitTest ("Abstract Fifo") {} class WriteThread : public Thread { public: WriteThread (AbstractFifo& f, int* b, Random rng) : Thread ("fifo writer"), fifo (f), buffer (b), random (rng) { startThread(); } ~WriteThread() { stopThread (5000); } void run() { int n = 0; while (! threadShouldExit()) { int num = random.nextInt (2000) + 1; int start1, size1, start2, size2; fifo.prepareToWrite (num, start1, size1, start2, size2); jassert (size1 >= 0 && size2 >= 0); jassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize())); jassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())); for (int i = 0; i < size1; ++i) buffer [start1 + i] = n++; for (int i = 0; i < size2; ++i) buffer [start2 + i] = n++; fifo.finishedWrite (size1 + size2); } } private: AbstractFifo& fifo; int* buffer; Random random; }; void runTest() { beginTest ("AbstractFifo"); int buffer [5000]; AbstractFifo fifo (numElementsInArray (buffer)); WriteThread writer (fifo, buffer, getRandom()); int n = 0; Random r = getRandom(); r.combineSeed (12345); for (int count = 100000; --count >= 0;) { int num = r.nextInt (6000) + 1; int start1, size1, start2, size2; fifo.prepareToRead (num, start1, size1, start2, size2); if (! (size1 >= 0 && size2 >= 0) && (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize())) && (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize()))) { expect (false, "prepareToRead returned -ve values"); break; } bool failed = false; for (int i = 0; i < size1; ++i) failed = (buffer [start1 + i] != n++) || failed; for (int i = 0; i < size2; ++i) failed = (buffer [start2 + i] != n++) || failed; if (failed) { expect (false, "read values were incorrect"); break; } fifo.finishedRead (size1 + size2); } } }; static AbstractFifoTests fifoUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h000066400000000000000000000224161320201440200314420ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_ABSTRACTFIFO_H_INCLUDED #define JUCE_ABSTRACTFIFO_H_INCLUDED //============================================================================== /** Encapsulates the logic required to implement a lock-free FIFO. This class handles the logic needed when building a single-reader, single-writer FIFO. It doesn't actually hold any data itself, but your FIFO class can use one of these to manage its position and status when reading or writing to it. To use it, you can call prepareToWrite() to determine the position within your own buffer that an incoming block of data should be stored, and prepareToRead() to find out when the next outgoing block should be read from. e.g. @code class MyFifo { public: MyFifo() : abstractFifo (1024) { } void addToFifo (const int* someData, int numItems) { int start1, size1, start2, size2; abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2); if (size1 > 0) copySomeData (myBuffer + start1, someData, size1); if (size2 > 0) copySomeData (myBuffer + start2, someData + size1, size2); abstractFifo.finishedWrite (size1 + size2); } void readFromFifo (int* someData, int numItems) { int start1, size1, start2, size2; abstractFifo.prepareToRead (numItems, start1, size1, start2, size2); if (size1 > 0) copySomeData (someData, myBuffer + start1, size1); if (size2 > 0) copySomeData (someData + size1, myBuffer + start2, size2); abstractFifo.finishedRead (size1 + size2); } private: AbstractFifo abstractFifo; int myBuffer [1024]; }; @endcode */ class JUCE_API AbstractFifo { public: //============================================================================== /** Creates a FIFO to manage a buffer with the specified capacity. */ AbstractFifo (int capacity) noexcept; /** Destructor */ ~AbstractFifo(); //============================================================================== /** Returns the total size of the buffer being managed. */ int getTotalSize() const noexcept; /** Returns the number of items that can currently be added to the buffer without it overflowing. */ int getFreeSpace() const noexcept; /** Returns the number of items that can currently be read from the buffer. */ int getNumReady() const noexcept; /** Clears the buffer positions, so that it appears empty. */ void reset() noexcept; /** Changes the buffer's total size. Note that this isn't thread-safe, so don't call it if there's any danger that it might overlap with a call to any other method in this class! */ void setTotalSize (int newSize) noexcept; //============================================================================== /** Returns the location within the buffer at which an incoming block of data should be written. Because the section of data that you want to add to the buffer may overlap the end and wrap around to the start, two blocks within your buffer are returned, and you should copy your data into the first one, with any remaining data spilling over into the second. If the number of items you ask for is too large to fit within the buffer's free space, then blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you may decide to keep waiting and re-trying the method until there's enough space available. After calling this method, if you choose to write your data into the blocks returned, you must call finishedWrite() to tell the FIFO how much data you actually added. e.g. @code void addToFifo (const int* someData, int numItems) { int start1, size1, start2, size2; prepareToWrite (numItems, start1, size1, start2, size2); if (size1 > 0) copySomeData (myBuffer + start1, someData, size1); if (size2 > 0) copySomeData (myBuffer + start2, someData + size1, size2); finishedWrite (size1 + size2); } @endcode @param numToWrite indicates how many items you'd like to add to the buffer @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into the first block should be written @param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2 @see finishedWrite */ void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept; /** Called after writing from the FIFO, to indicate that this many items have been added. @see prepareToWrite */ void finishedWrite (int numWritten) noexcept; /** Returns the location within the buffer from which the next block of data should be read. Because the section of data that you want to read from the buffer may overlap the end and wrap around to the start, two blocks within your buffer are returned, and you should read from both of them. If the number of items you ask for is greater than the amount of data available, then blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you may decide to keep waiting and re-trying the method until there's enough data available. After calling this method, if you choose to read the data, you must call finishedRead() to tell the FIFO how much data you have consumed. e.g. @code void readFromFifo (int* someData, int numItems) { int start1, size1, start2, size2; prepareToRead (numSamples, start1, size1, start2, size2); if (size1 > 0) copySomeData (someData, myBuffer + start1, size1); if (size2 > 0) copySomeData (someData + size1, myBuffer + start2, size2); finishedRead (size1 + size2); } @endcode @param numWanted indicates how many items you'd like to add to the buffer @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into the first block should be written @param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2 @see finishedRead */ void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept; /** Called after reading from the FIFO, to indicate that this many items have now been consumed. @see prepareToRead */ void finishedRead (int numRead) noexcept; private: //============================================================================== int bufferSize; Atomic validStart, validEnd; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo) }; #endif // JUCE_ABSTRACTFIFO_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_Array.h000066400000000000000000001232741320201440200301550ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_ARRAY_H_INCLUDED #define JUCE_ARRAY_H_INCLUDED //============================================================================== /** Holds a resizable array of primitive or copy-by-value objects. Examples of arrays are: Array, Array or Array The Array class can be used to hold simple, non-polymorphic objects as well as primitive types - to do so, the class must fulfil these requirements: - it must have a copy constructor and assignment operator - it must be able to be relocated in memory by a memcpy without this causing any problems - so objects whose functionality relies on external pointers or references to themselves can not be used. You can of course have an array of pointers to any kind of object, e.g. Array, but if you do this, the array doesn't take any ownership of the objects - see the OwnedArray class or the ReferenceCountedArray class for more powerful ways of holding lists of objects. For holding lists of strings, you can use Array\, but it's usually better to use the specialised class StringArray, which provides more useful functions. To make all the array's methods thread-safe, pass in "CriticalSection" as the templated TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. @see OwnedArray, ReferenceCountedArray, StringArray, CriticalSection */ template class Array { private: typedef PARAMETER_TYPE (ElementType) ParameterType; public: //============================================================================== /** Creates an empty array. */ Array() noexcept : numUsed (0) { } /** Creates a copy of another array. @param other the array to copy */ Array (const Array& other) { const ScopedLockType lock (other.getLock()); numUsed = other.numUsed; data.setAllocatedSize (other.numUsed); for (int i = 0; i < numUsed; ++i) new (data.elements + i) ElementType (other.data.elements[i]); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Array (Array&& other) noexcept : data (static_cast&&> (other.data)), numUsed (other.numUsed) { other.numUsed = 0; } #endif /** Initalises from a null-terminated C array of values. @param values the array to copy from */ template explicit Array (const TypeToCreateFrom* values) : numUsed (0) { while (*values != TypeToCreateFrom()) add (*values++); } /** Initalises from a C array of values. @param values the array to copy from @param numValues the number of values in the array */ template Array (const TypeToCreateFrom* values, int numValues) : numUsed (numValues) { data.setAllocatedSize (numValues); for (int i = 0; i < numValues; ++i) new (data.elements + i) ElementType (values[i]); } #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS template Array (const std::initializer_list& items) : numUsed (0) { addArray (items); } #endif /** Destructor. */ ~Array() { deleteAllElements(); } /** Copies another array. @param other the array to copy */ Array& operator= (const Array& other) { if (this != &other) { Array otherCopy (other); swapWith (otherCopy); } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Array& operator= (Array&& other) noexcept { const ScopedLockType lock (getLock()); deleteAllElements(); data = static_cast&&> (other.data); numUsed = other.numUsed; other.numUsed = 0; return *this; } #endif //============================================================================== /** Compares this array to another one. Two arrays are considered equal if they both contain the same set of elements, in the same order. @param other the other array to compare with */ template bool operator== (const OtherArrayType& other) const { const ScopedLockType lock (getLock()); const typename OtherArrayType::ScopedLockType lock2 (other.getLock()); if (numUsed != other.numUsed) return false; for (int i = numUsed; --i >= 0;) if (! (data.elements [i] == other.data.elements [i])) return false; return true; } /** Compares this array to another one. Two arrays are considered equal if they both contain the same set of elements, in the same order. @param other the other array to compare with */ template bool operator!= (const OtherArrayType& other) const { return ! operator== (other); } //============================================================================== /** Removes all elements from the array. This will remove all the elements, and free any storage that the array is using. To clear the array without freeing the storage, use the clearQuick() method instead. @see clearQuick */ void clear() { const ScopedLockType lock (getLock()); deleteAllElements(); data.setAllocatedSize (0); numUsed = 0; } /** Removes all elements from the array without freeing the array's allocated storage. @see clear */ void clearQuick() { const ScopedLockType lock (getLock()); deleteAllElements(); numUsed = 0; } //============================================================================== /** Returns the current number of elements in the array. */ inline int size() const noexcept { return numUsed; } /** Returns one of the elements in the array. If the index passed in is beyond the range of valid elements, this will return a default value. If you're certain that the index will always be a valid element, you can call getUnchecked() instead, which is faster. @param index the index of the element being requested (0 is the first element in the array) @see getUnchecked, getFirst, getLast */ ElementType operator[] (const int index) const { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (index, numUsed)) { jassert (data.elements != nullptr); return data.elements [index]; } return ElementType(); } /** Returns one of the elements in the array, without checking the index passed in. Unlike the operator[] method, this will try to return an element without checking that the index is within the bounds of the array, so should only be used when you're confident that it will always be a valid index. @param index the index of the element being requested (0 is the first element in the array) @see operator[], getFirst, getLast */ inline ElementType getUnchecked (const int index) const { const ScopedLockType lock (getLock()); jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } /** Returns a direct reference to one of the elements in the array, without checking the index passed in. This is like getUnchecked, but returns a direct reference to the element, so that you can alter it directly. Obviously this can be dangerous, so only use it when absolutely necessary. @param index the index of the element being requested (0 is the first element in the array) @see operator[], getFirst, getLast */ inline ElementType& getReference (const int index) const noexcept { const ScopedLockType lock (getLock()); jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } /** Returns the first element in the array, or a default value if the array is empty. @see operator[], getUnchecked, getLast */ inline ElementType getFirst() const { const ScopedLockType lock (getLock()); if (numUsed > 0) { jassert (data.elements != nullptr); return data.elements[0]; } return ElementType(); } /** Returns the last element in the array, or a default value if the array is empty. @see operator[], getUnchecked, getFirst */ inline ElementType getLast() const { const ScopedLockType lock (getLock()); if (numUsed > 0) { jassert (data.elements != nullptr); return data.elements[numUsed - 1]; } return ElementType(); } /** Returns a pointer to the actual array data. This pointer will only be valid until the next time a non-const method is called on the array. */ inline ElementType* getRawDataPointer() noexcept { return data.elements; } //============================================================================== /** Returns a pointer to the first element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ElementType* begin() const noexcept { return data.elements; } /** Returns a pointer to the element which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ElementType* end() const noexcept { #if JUCE_DEBUG if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy) return data.elements; #endif return data.elements + numUsed; } //============================================================================== /** Finds the index of the first element which matches the value passed in. This will search the array for the given object, and return the index of its first occurrence. If the object isn't found, the method will return -1. @param elementToLookFor the value or object to look for @returns the index of the object, or -1 if it's not found */ int indexOf (ParameterType elementToLookFor) const { const ScopedLockType lock (getLock()); const ElementType* e = data.elements.getData(); const ElementType* const end_ = e + numUsed; for (; e != end_; ++e) if (elementToLookFor == *e) return static_cast (e - data.elements.getData()); return -1; } /** Returns true if the array contains at least one occurrence of an object. @param elementToLookFor the value or object to look for @returns true if the item is found */ bool contains (ParameterType elementToLookFor) const { const ScopedLockType lock (getLock()); const ElementType* e = data.elements.getData(); const ElementType* const end_ = e + numUsed; for (; e != end_; ++e) if (elementToLookFor == *e) return true; return false; } //============================================================================== /** Appends a new element at the end of the array. @param newElement the new object to add to the array @see set, insert, addIfNotAlreadyThere, addSorted, addUsingDefaultSort, addArray */ void add (const ElementType& newElement) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); new (data.elements + numUsed++) ElementType (newElement); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS /** Appends a new element at the end of the array. @param newElement the new object to add to the array @see set, insert, addIfNotAlreadyThere, addSorted, addUsingDefaultSort, addArray */ void add (ElementType&& newElement) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); new (data.elements + numUsed++) ElementType (static_cast (newElement)); } #endif /** Inserts a new element into the array at a given position. If the index is less than 0 or greater than the size of the array, the element will be added to the end of the array. Otherwise, it will be inserted into the array, moving all the later elements along to make room. @param indexToInsertAt the index at which the new element should be inserted (pass in -1 to add it to the end) @param newElement the new object to add to the array @see add, addSorted, addUsingDefaultSort, set */ void insert (int indexToInsertAt, ParameterType newElement) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); jassert (data.elements != nullptr); if (isPositiveAndBelow (indexToInsertAt, numUsed)) { ElementType* const insertPos = data.elements + indexToInsertAt; const int numberToMove = numUsed - indexToInsertAt; if (numberToMove > 0) memmove (insertPos + 1, insertPos, ((size_t) numberToMove) * sizeof (ElementType)); new (insertPos) ElementType (newElement); ++numUsed; } else { new (data.elements + numUsed++) ElementType (newElement); } } /** Inserts multiple copies of an element into the array at a given position. If the index is less than 0 or greater than the size of the array, the element will be added to the end of the array. Otherwise, it will be inserted into the array, moving all the later elements along to make room. @param indexToInsertAt the index at which the new element should be inserted @param newElement the new object to add to the array @param numberOfTimesToInsertIt how many copies of the value to insert @see insert, add, addSorted, set */ void insertMultiple (int indexToInsertAt, ParameterType newElement, int numberOfTimesToInsertIt) { if (numberOfTimesToInsertIt > 0) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + numberOfTimesToInsertIt); ElementType* insertPos; if (isPositiveAndBelow (indexToInsertAt, numUsed)) { insertPos = data.elements + indexToInsertAt; const int numberToMove = numUsed - indexToInsertAt; memmove (insertPos + numberOfTimesToInsertIt, insertPos, ((size_t) numberToMove) * sizeof (ElementType)); } else { insertPos = data.elements + numUsed; } numUsed += numberOfTimesToInsertIt; while (--numberOfTimesToInsertIt >= 0) { new (insertPos) ElementType (newElement); ++insertPos; // NB: this increment is done separately from the // new statement to avoid a compiler bug in VS2014 } } } /** Inserts an array of values into this array at a given position. If the index is less than 0 or greater than the size of the array, the new elements will be added to the end of the array. Otherwise, they will be inserted into the array, moving all the later elements along to make room. @param indexToInsertAt the index at which the first new element should be inserted @param newElements the new values to add to the array @param numberOfElements how many items are in the array @see insert, add, addSorted, set */ void insertArray (int indexToInsertAt, const ElementType* newElements, int numberOfElements) { if (numberOfElements > 0) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + numberOfElements); ElementType* insertPos = data.elements; if (isPositiveAndBelow (indexToInsertAt, numUsed)) { insertPos += indexToInsertAt; const int numberToMove = numUsed - indexToInsertAt; memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ElementType)); } else { insertPos += numUsed; } numUsed += numberOfElements; while (--numberOfElements >= 0) new (insertPos++) ElementType (*newElements++); } } /** Appends a new element at the end of the array as long as the array doesn't already contain it. If the array already contains an element that matches the one passed in, nothing will be done. @param newElement the new object to add to the array */ void addIfNotAlreadyThere (ParameterType newElement) { const ScopedLockType lock (getLock()); if (! contains (newElement)) add (newElement); } /** Replaces an element with a new value. If the index is less than zero, this method does nothing. If the index is beyond the end of the array, the item is added to the end of the array. @param indexToChange the index whose value you want to change @param newValue the new value to set for this index. @see add, insert */ void set (const int indexToChange, ParameterType newValue) { jassert (indexToChange >= 0); const ScopedLockType lock (getLock()); if (isPositiveAndBelow (indexToChange, numUsed)) { jassert (data.elements != nullptr); data.elements [indexToChange] = newValue; } else if (indexToChange >= 0) { data.ensureAllocatedSize (numUsed + 1); new (data.elements + numUsed++) ElementType (newValue); } } /** Replaces an element with a new value without doing any bounds-checking. This just sets a value directly in the array's internal storage, so you'd better make sure it's in range! @param indexToChange the index whose value you want to change @param newValue the new value to set for this index. @see set, getUnchecked */ void setUnchecked (const int indexToChange, ParameterType newValue) { const ScopedLockType lock (getLock()); jassert (isPositiveAndBelow (indexToChange, numUsed)); data.elements [indexToChange] = newValue; } /** Adds elements from an array to the end of this array. @param elementsToAdd an array of some kind of object from which elements can be constructed. @param numElementsToAdd how many elements are in this other array @see add */ template void addArray (const Type* elementsToAdd, int numElementsToAdd) { const ScopedLockType lock (getLock()); if (numElementsToAdd > 0) { data.ensureAllocatedSize (numUsed + numElementsToAdd); while (--numElementsToAdd >= 0) { new (data.elements + numUsed) ElementType (*elementsToAdd++); ++numUsed; } } } #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS template void addArray (const std::initializer_list& items) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + (int) items.size()); for (auto& item : items) { new (data.elements + numUsed) ElementType (item); ++numUsed; } } #endif /** Adds elements from a null-terminated array of pointers to the end of this array. @param elementsToAdd an array of pointers to some kind of object from which elements can be constructed. This array must be terminated by a nullptr @see addArray */ template void addNullTerminatedArray (const Type* const* elementsToAdd) { int num = 0; for (const Type* const* e = elementsToAdd; *e != nullptr; ++e) ++num; addArray (elementsToAdd, num); } /** This swaps the contents of this array with those of another array. If you need to exchange two arrays, this is vastly quicker than using copy-by-value because it just swaps their internal pointers. */ template void swapWith (OtherArrayType& otherArray) noexcept { const ScopedLockType lock1 (getLock()); const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); data.swapWith (otherArray.data); std::swap (numUsed, otherArray.numUsed); } /** Adds elements from another array to the end of this array. @param arrayToAddFrom the array from which to copy the elements @param startIndex the first element of the other array to start copying from @param numElementsToAdd how many elements to add from the other array. If this value is negative or greater than the number of available elements, all available elements will be copied. @see add */ template void addArray (const OtherArrayType& arrayToAddFrom, int startIndex = 0, int numElementsToAdd = -1) { const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock()); { const ScopedLockType lock2 (getLock()); if (startIndex < 0) { jassertfalse; startIndex = 0; } if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) numElementsToAdd = arrayToAddFrom.size() - startIndex; while (--numElementsToAdd >= 0) add (arrayToAddFrom.getUnchecked (startIndex++)); } } /** This will enlarge or shrink the array to the given number of elements, by adding or removing items from its end. If the array is smaller than the given target size, empty elements will be appended until its size is as specified. If its size is larger than the target, items will be removed from its end to shorten it. */ void resize (const int targetNumItems) { jassert (targetNumItems >= 0); const int numToAdd = targetNumItems - numUsed; if (numToAdd > 0) insertMultiple (numUsed, ElementType(), numToAdd); else if (numToAdd < 0) removeRange (targetNumItems, -numToAdd); } /** Inserts a new element into the array, assuming that the array is sorted. This will use a comparator to find the position at which the new element should go. If the array isn't sorted, the behaviour of this method will be unpredictable. @param comparator the comparator to use to compare the elements - see the sort() method for details about the form this object should take @param newElement the new element to insert to the array @returns the index at which the new item was added @see addUsingDefaultSort, add, sort */ template int addSorted (ElementComparator& comparator, ParameterType newElement) { const ScopedLockType lock (getLock()); const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newElement, 0, numUsed); insert (index, newElement); return index; } /** Inserts a new element into the array, assuming that the array is sorted. This will use the DefaultElementComparator class for sorting, so your ElementType must be suitable for use with that class. If the array isn't sorted, the behaviour of this method will be unpredictable. @param newElement the new element to insert to the array @see addSorted, sort */ void addUsingDefaultSort (ParameterType newElement) { DefaultElementComparator comparator; addSorted (comparator, newElement); } /** Finds the index of an element in the array, assuming that the array is sorted. This will use a comparator to do a binary-chop to find the index of the given element, if it exists. If the array isn't sorted, the behaviour of this method will be unpredictable. @param comparator the comparator to use to compare the elements - see the sort() method for details about the form this object should take @param elementToLookFor the element to search for @returns the index of the element, or -1 if it's not found @see addSorted, sort */ template int indexOfSorted (ElementComparator& comparator, TargetValueType elementToLookFor) const { (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); for (int s = 0, e = numUsed;;) { if (s >= e) return -1; if (comparator.compareElements (elementToLookFor, data.elements [s]) == 0) return s; const int halfway = (s + e) / 2; if (halfway == s) return -1; if (comparator.compareElements (elementToLookFor, data.elements [halfway]) >= 0) s = halfway; else e = halfway; } } //============================================================================== /** Removes an element from the array. This will remove the element at a given index, and move back all the subsequent elements to close the gap. If the index passed in is out-of-range, nothing will happen. @param indexToRemove the index of the element to remove @returns the element that has been removed @see removeFirstMatchingValue, removeAllInstancesOf, removeRange */ ElementType remove (const int indexToRemove) { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (indexToRemove, numUsed)) { jassert (data.elements != nullptr); ElementType removed (data.elements[indexToRemove]); removeInternal (indexToRemove); return removed; } return ElementType(); } /** Removes an item from the array. This will remove the first occurrence of the given element from the array. If the item isn't found, no action is taken. @param valueToRemove the object to try to remove @see remove, removeRange */ void removeFirstMatchingValue (ParameterType valueToRemove) { const ScopedLockType lock (getLock()); ElementType* const e = data.elements; for (int i = 0; i < numUsed; ++i) { if (valueToRemove == e[i]) { removeInternal (i); break; } } } /** Removes an item from the array. This will remove all occurrences of the given element from the array. If no such items are found, no action is taken. @param valueToRemove the object to try to remove @see remove, removeRange */ void removeAllInstancesOf (ParameterType valueToRemove) { const ScopedLockType lock (getLock()); for (int i = numUsed; --i >= 0;) if (valueToRemove == data.elements[i]) removeInternal (i); } /** Removes a range of elements from the array. This will remove a set of elements, starting from the given index, and move subsequent elements down to close the gap. If the range extends beyond the bounds of the array, it will be safely clipped to the size of the array. @param startIndex the index of the first element to remove @param numberToRemove how many elements should be removed @see remove, removeFirstMatchingValue, removeAllInstancesOf */ void removeRange (int startIndex, int numberToRemove) { const ScopedLockType lock (getLock()); const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); startIndex = jlimit (0, numUsed, startIndex); if (endIndex > startIndex) { ElementType* const e = data.elements + startIndex; numberToRemove = endIndex - startIndex; for (int i = 0; i < numberToRemove; ++i) e[i].~ElementType(); const int numToShift = numUsed - endIndex; if (numToShift > 0) memmove (e, e + numberToRemove, ((size_t) numToShift) * sizeof (ElementType)); numUsed -= numberToRemove; minimiseStorageAfterRemoval(); } } /** Removes the last n elements from the array. @param howManyToRemove how many elements to remove from the end of the array @see remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ void removeLast (int howManyToRemove = 1) { const ScopedLockType lock (getLock()); if (howManyToRemove > numUsed) howManyToRemove = numUsed; for (int i = 1; i <= howManyToRemove; ++i) data.elements [numUsed - i].~ElementType(); numUsed -= howManyToRemove; minimiseStorageAfterRemoval(); } /** Removes any elements which are also in another array. @param otherArray the other array in which to look for elements to remove @see removeValuesNotIn, remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ template void removeValuesIn (const OtherArrayType& otherArray) { const typename OtherArrayType::ScopedLockType lock1 (otherArray.getLock()); const ScopedLockType lock2 (getLock()); if (this == &otherArray) { clear(); } else { if (otherArray.size() > 0) { for (int i = numUsed; --i >= 0;) if (otherArray.contains (data.elements [i])) removeInternal (i); } } } /** Removes any elements which are not found in another array. Only elements which occur in this other array will be retained. @param otherArray the array in which to look for elements NOT to remove @see removeValuesIn, remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ template void removeValuesNotIn (const OtherArrayType& otherArray) { const typename OtherArrayType::ScopedLockType lock1 (otherArray.getLock()); const ScopedLockType lock2 (getLock()); if (this != &otherArray) { if (otherArray.size() <= 0) { clear(); } else { for (int i = numUsed; --i >= 0;) if (! otherArray.contains (data.elements [i])) removeInternal (i); } } } /** Swaps over two elements in the array. This swaps over the elements found at the two indexes passed in. If either index is out-of-range, this method will do nothing. @param index1 index of one of the elements to swap @param index2 index of the other element to swap */ void swap (const int index1, const int index2) { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (index1, numUsed) && isPositiveAndBelow (index2, numUsed)) { std::swap (data.elements [index1], data.elements [index2]); } } /** Moves one of the values to a different position. This will move the value to a specified index, shuffling along any intervening elements as required. So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. @param currentIndex the index of the value to be moved. If this isn't a valid index, then nothing will be done @param newIndex the index at which you'd like this value to end up. If this is less than zero, the value will be moved to the end of the array */ void move (const int currentIndex, int newIndex) noexcept { if (currentIndex != newIndex) { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (currentIndex, numUsed)) { if (! isPositiveAndBelow (newIndex, numUsed)) newIndex = numUsed - 1; char tempCopy [sizeof (ElementType)]; memcpy (tempCopy, data.elements + currentIndex, sizeof (ElementType)); if (newIndex > currentIndex) { memmove (data.elements + currentIndex, data.elements + currentIndex + 1, sizeof (ElementType) * (size_t) (newIndex - currentIndex)); } else { memmove (data.elements + newIndex + 1, data.elements + newIndex, sizeof (ElementType) * (size_t) (currentIndex - newIndex)); } memcpy (data.elements + newIndex, tempCopy, sizeof (ElementType)); } } } //============================================================================== /** Reduces the amount of storage being used by the array. Arrays typically allocate slightly more storage than they need, and after removing elements, they may have quite a lot of unused space allocated. This method will reduce the amount of allocated storage to a minimum. */ void minimiseStorageOverheads() { const ScopedLockType lock (getLock()); data.shrinkToNoMoreThan (numUsed); } /** Increases the array's internal storage to hold a minimum number of elements. Calling this before adding a large known number of elements means that the array won't have to keep dynamically resizing itself as the elements are added, and it'll therefore be more efficient. */ void ensureStorageAllocated (const int minNumElements) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (minNumElements); } //============================================================================== /** Sorts the elements in the array. This will use a comparator object to sort the elements into order. The object passed must have a method of the form: @code int compareElements (ElementType first, ElementType second); @endcode ..and this method must return: - a value of < 0 if the first comes before the second - a value of 0 if the two objects are equivalent - a value of > 0 if the second comes before the first To improve performance, the compareElements() method can be declared as static or const. @param comparator the comparator to use for comparing elements. @param retainOrderOfEquivalentItems if this is true, then items which the comparator says are equivalent will be kept in the order in which they currently appear in the array. This is slower to perform, but may be important in some cases. If it's false, a faster algorithm is used, but equivalent elements may be rearranged. @see addSorted, indexOfSorted, sortArray */ template void sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false) const { const ScopedLockType lock (getLock()); (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); } //============================================================================== /** Returns the CriticalSection that locks this array. To lock, you can call getLock().enter() and getLock().exit(), or preferably use an object of ScopedLockType as an RAII lock for it. */ inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; } /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; //============================================================================== #ifndef DOXYGEN // Note that the swapWithArray method has been replaced by a more flexible templated version, // and renamed "swapWith" to be more consistent with the names used in other classes. JUCE_DEPRECATED_WITH_BODY (void swapWithArray (Array& other) noexcept, { swapWith (other); }) #endif private: //============================================================================== ArrayAllocationBase data; int numUsed; void removeInternal (const int indexToRemove) { --numUsed; ElementType* const e = data.elements + indexToRemove; e->~ElementType(); const int numberToShift = numUsed - indexToRemove; if (numberToShift > 0) memmove (e, e + 1, ((size_t) numberToShift) * sizeof (ElementType)); minimiseStorageAfterRemoval(); } inline void deleteAllElements() noexcept { for (int i = 0; i < numUsed; ++i) data.elements[i].~ElementType(); } void minimiseStorageAfterRemoval() { if (data.numAllocated > jmax (minimumAllocatedSize, numUsed * 2)) data.shrinkToNoMoreThan (jmax (numUsed, jmax (minimumAllocatedSize, 64 / (int) sizeof (ElementType)))); } }; #endif // JUCE_ARRAY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h000066400000000000000000000120141320201440200327430ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_ARRAYALLOCATIONBASE_H_INCLUDED #define JUCE_ARRAYALLOCATIONBASE_H_INCLUDED //============================================================================== /** Implements some basic array storage allocation functions. This class isn't really for public use - it's used by the other array classes, but might come in handy for some purposes. It inherits from a critical section class to allow the arrays to use the "empty base class optimisation" pattern to reduce their footprint. @see Array, OwnedArray, ReferenceCountedArray */ template class ArrayAllocationBase : public TypeOfCriticalSectionToUse { public: //============================================================================== /** Creates an empty array. */ ArrayAllocationBase() noexcept : numAllocated (0) { } /** Destructor. */ ~ArrayAllocationBase() noexcept { } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS ArrayAllocationBase (ArrayAllocationBase&& other) noexcept : elements (static_cast &&> (other.elements)), numAllocated (other.numAllocated) { } ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept { elements = static_cast &&> (other.elements); numAllocated = other.numAllocated; return *this; } #endif //============================================================================== /** Changes the amount of storage allocated. This will retain any data currently held in the array, and either add or remove extra space at the end. @param numElements the number of elements that are needed */ void setAllocatedSize (const int numElements) { if (numAllocated != numElements) { if (numElements > 0) elements.realloc ((size_t) numElements); else elements.free(); numAllocated = numElements; } } /** Increases the amount of storage allocated if it is less than a given amount. This will retain any data currently held in the array, but will add extra space at the end to make sure there it's at least as big as the size passed in. If it's already bigger, no action is taken. @param minNumElements the minimum number of elements that are needed */ void ensureAllocatedSize (const int minNumElements) { if (minNumElements > numAllocated) setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7); jassert (numAllocated <= 0 || elements != nullptr); } /** Minimises the amount of storage allocated so that it's no more than the given number of elements. */ void shrinkToNoMoreThan (const int maxNumElements) { if (maxNumElements < numAllocated) setAllocatedSize (maxNumElements); } /** Swap the contents of two objects. */ void swapWith (ArrayAllocationBase & other) noexcept { elements.swapWith (other.elements); std::swap (numAllocated, other.numAllocated); } //============================================================================== HeapBlock elements; int numAllocated; private: JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase) }; #endif // JUCE_ARRAYALLOCATIONBASE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp000066400000000000000000000076771320201440200321550ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ DynamicObject::DynamicObject() { } DynamicObject::DynamicObject (const DynamicObject& other) : ReferenceCountedObject(), properties (other.properties) { } DynamicObject::~DynamicObject() { } bool DynamicObject::hasProperty (const Identifier& propertyName) const { const var* const v = properties.getVarPointer (propertyName); return v != nullptr && ! v->isMethod(); } const var& DynamicObject::getProperty (const Identifier& propertyName) const { return properties [propertyName]; } void DynamicObject::setProperty (const Identifier& propertyName, const var& newValue) { properties.set (propertyName, newValue); } void DynamicObject::removeProperty (const Identifier& propertyName) { properties.remove (propertyName); } bool DynamicObject::hasMethod (const Identifier& methodName) const { return getProperty (methodName).isMethod(); } var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArgs& args) { if (var::NativeFunction function = properties [method].getNativeFunction()) return function (args); return var(); } void DynamicObject::setMethod (Identifier name, var::NativeFunction function) { properties.set (name, var (function)); } void DynamicObject::clear() { properties.clear(); } void DynamicObject::cloneAllProperties() { for (int i = properties.size(); --i >= 0;) if (var* v = properties.getVarPointerAt (i)) *v = v->clone(); } DynamicObject::Ptr DynamicObject::clone() { Ptr d (new DynamicObject (*this)); d->cloneAllProperties(); return d; } void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine) { out << '{'; if (! allOnOneLine) out << newLine; const int numValues = properties.size(); for (int i = 0; i < numValues; ++i) { if (! allOnOneLine) JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); out << '"'; JSONFormatter::writeString (out, properties.getName (i)); out << "\": "; JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine); if (i < numValues - 1) { if (allOnOneLine) out << ", "; else out << ',' << newLine; } else if (! allOnOneLine) out << newLine; } if (! allOnOneLine) JSONFormatter::writeSpaces (out, indentLevel); out << '}'; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h000066400000000000000000000134411320201440200316040ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_DYNAMICOBJECT_H_INCLUDED #define JUCE_DYNAMICOBJECT_H_INCLUDED //============================================================================== /** Represents a dynamically implemented object. This class is primarily intended for wrapping scripting language objects, but could be used for other purposes. An instance of a DynamicObject can be used to store named properties, and by subclassing hasMethod() and invokeMethod(), you can give your object methods. */ class JUCE_API DynamicObject : public ReferenceCountedObject { public: //============================================================================== DynamicObject(); DynamicObject (const DynamicObject&); ~DynamicObject(); typedef ReferenceCountedObjectPtr Ptr; //============================================================================== /** Returns true if the object has a property with this name. Note that if the property is actually a method, this will return false. */ virtual bool hasProperty (const Identifier& propertyName) const; /** Returns a named property. This returns var::null if no such property exists. */ virtual const var& getProperty (const Identifier& propertyName) const; /** Sets a named property. */ virtual void setProperty (const Identifier& propertyName, const var& newValue); /** Removes a named property. */ virtual void removeProperty (const Identifier& propertyName); //============================================================================== /** Checks whether this object has the specified method. The default implementation of this just checks whether there's a property with this name that's actually a method, but this can be overridden for building objects with dynamic invocation. */ virtual bool hasMethod (const Identifier& methodName) const; /** Invokes a named method on this object. The default implementation looks up the named property, and if it's a method call, then it invokes it. This method is virtual to allow more dynamic invocation to used for objects where the methods may not already be set as properies. */ virtual var invokeMethod (Identifier methodName, const var::NativeFunctionArgs& args); /** Adds a method to the class. This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but helps to avoid accidentally invoking the wrong type of var constructor. It also makes the code easier to read, */ void setMethod (Identifier methodName, var::NativeFunction function); //============================================================================== /** Removes all properties and methods from the object. */ void clear(); /** Returns the NamedValueSet that holds the object's properties. */ NamedValueSet& getProperties() noexcept { return properties; } /** Calls var::clone() on all the properties that this object contains. */ void cloneAllProperties(); //============================================================================== /** Returns a clone of this object. The default implementation of this method just returns a new DynamicObject with a (deep) copy of all of its properties. Subclasses can override this to implement their own custom copy routines. */ virtual Ptr clone(); //============================================================================== /** Writes this object to a text stream in JSON format. This method is used by JSON::toString and JSON::writeToStream, and you should never need to call it directly, but it's virtual so that custom object types can stringify themselves appropriately. */ virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine); private: //============================================================================== NamedValueSet properties; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // These methods have been deprecated - use var::invoke instead virtual void invokeMethod (const Identifier&, const var*, int) {} #endif JUCE_LEAK_DETECTOR (DynamicObject) }; #endif // JUCE_DYNAMICOBJECT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h000066400000000000000000000164361320201440200325210ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_ELEMENTCOMPARATOR_H_INCLUDED #define JUCE_ELEMENTCOMPARATOR_H_INCLUDED #ifndef DOXYGEN /** This is an internal helper class which converts a juce ElementComparator style class (using a "compareElements" method) into a class that's compatible with std::sort (i.e. using an operator() to compare the elements) */ template struct SortFunctionConverter { SortFunctionConverter (ElementComparator& e) : comparator (e) {} template bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; } private: ElementComparator& comparator; SortFunctionConverter& operator= (const SortFunctionConverter&) JUCE_DELETED_FUNCTION; }; #endif //============================================================================== /** Sorts a range of elements in an array. The comparator object that is passed-in must define a public method with the following signature: @code int compareElements (ElementType first, ElementType second); @endcode ..and this method must return: - a value of < 0 if the first comes before the second - a value of 0 if the two objects are equivalent - a value of > 0 if the second comes before the first To improve performance, the compareElements() method can be declared as static or const. @param comparator an object which defines a compareElements() method @param array the array to sort @param firstElement the index of the first element of the range to be sorted @param lastElement the index of the last element in the range that needs sorting (this is inclusive) @param retainOrderOfEquivalentItems if true, the order of items that the comparator deems the same will be maintained - this will be a slower algorithm than if they are allowed to be moved around. @see sortArrayRetainingOrder */ template static void sortArray (ElementComparator& comparator, ElementType* const array, int firstElement, int lastElement, const bool retainOrderOfEquivalentItems) { SortFunctionConverter converter (comparator); if (retainOrderOfEquivalentItems) std::stable_sort (array + firstElement, array + lastElement + 1, converter); else std::sort (array + firstElement, array + lastElement + 1, converter); } //============================================================================== /** Searches a sorted array of elements, looking for the index at which a specified value should be inserted for it to be in the correct order. The comparator object that is passed-in must define a public method with the following signature: @code int compareElements (ElementType first, ElementType second); @endcode ..and this method must return: - a value of < 0 if the first comes before the second - a value of 0 if the two objects are equivalent - a value of > 0 if the second comes before the first To improve performance, the compareElements() method can be declared as static or const. @param comparator an object which defines a compareElements() method @param array the array to search @param newElement the value that is going to be inserted @param firstElement the index of the first element to search @param lastElement the index of the last element in the range (this is non-inclusive) */ template static int findInsertIndexInSortedArray (ElementComparator& comparator, ElementType* const array, const ElementType newElement, int firstElement, int lastElement) { jassert (firstElement <= lastElement); (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused while (firstElement < lastElement) { if (comparator.compareElements (newElement, array [firstElement]) == 0) { ++firstElement; break; } else { const int halfway = (firstElement + lastElement) >> 1; if (halfway == firstElement) { if (comparator.compareElements (newElement, array [halfway]) >= 0) ++firstElement; break; } else if (comparator.compareElements (newElement, array [halfway]) >= 0) { firstElement = halfway; } else { lastElement = halfway; } } } return firstElement; } //============================================================================== /** A simple ElementComparator class that can be used to sort an array of objects that support the '<' operator. This will work for primitive types and objects that implement operator<(). Example: @code Array myArray; DefaultElementComparator sorter; myArray.sort (sorter); @endcode @see ElementComparator */ template class DefaultElementComparator { private: typedef PARAMETER_TYPE (ElementType) ParameterType; public: static int compareElements (ParameterType first, ParameterType second) { return (first < second) ? -1 : ((second < first) ? 1 : 0); } }; #endif // JUCE_ELEMENTCOMPARATOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h000066400000000000000000000403101320201440200304050ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_HASHMAP_H_INCLUDED #define JUCE_HASHMAP_H_INCLUDED //============================================================================== /** A simple class to generate hash functions for some primitive types, intended for use with the HashMap class. @see HashMap */ struct DefaultHashFunctions { /** Generates a simple hash from an integer. */ int generateHash (const int key, const int upperLimit) const noexcept { return std::abs (key) % upperLimit; } /** Generates a simple hash from an int64. */ int generateHash (const int64 key, const int upperLimit) const noexcept { return std::abs ((int) key) % upperLimit; } /** Generates a simple hash from a string. */ int generateHash (const String& key, const int upperLimit) const noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); } /** Generates a simple hash from a variant. */ int generateHash (const var& key, const int upperLimit) const noexcept { return generateHash (key.toString(), upperLimit); } }; //============================================================================== /** Holds a set of mappings between some key/value pairs. The types of the key and value objects are set as template parameters. You can also specify a class to supply a hash function that converts a key value into an hashed integer. This class must have the form: @code struct MyHashGenerator { int generateHash (MyKeyType key, int upperLimit) const { // The function must return a value 0 <= x < upperLimit return someFunctionOfMyKeyType (key) % upperLimit; } }; @endcode Like the Array class, the key and value types are expected to be copy-by-value types, so if you define them to be pointer types, this class won't delete the objects that they point to. If you don't supply a class for the HashFunctionType template parameter, the default one provides some simple mappings for strings and ints. @code HashMap hash; hash.set (1, "item1"); hash.set (2, "item2"); DBG (hash [1]); // prints "item1" DBG (hash [2]); // prints "item2" // This iterates the map, printing all of its key -> value pairs.. for (HashMap::Iterator i (hash); i.next();) DBG (i.getKey() << " -> " << i.getValue()); @endcode @tparam HashFunctionType The class of hash function, which must be copy-constructible. @see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet */ template class HashMap { private: typedef PARAMETER_TYPE (KeyType) KeyTypeParameter; typedef PARAMETER_TYPE (ValueType) ValueTypeParameter; public: //============================================================================== /** Creates an empty hash-map. @param numberOfSlots Specifies the number of hash entries the map will use. This will be the "upperLimit" parameter that is passed to your generateHash() function. The number of hash slots will grow automatically if necessary, or it can be remapped manually using remapTable(). @param hashFunction An instance of HashFunctionType, which will be copied and stored to use with the HashMap. This parameter can be omitted if HashFunctionType has a default constructor. */ explicit HashMap (int numberOfSlots = defaultHashTableSize, HashFunctionType hashFunction = HashFunctionType()) : hashFunctionToUse (hashFunction), totalNumItems (0) { hashSlots.insertMultiple (0, nullptr, numberOfSlots); } /** Destructor. */ ~HashMap() { clear(); } //============================================================================== /** Removes all values from the map. Note that this will clear the content, but won't affect the number of slots (see remapTable and getNumSlots). */ void clear() { const ScopedLockType sl (getLock()); for (int i = hashSlots.size(); --i >= 0;) { HashEntry* h = hashSlots.getUnchecked(i); while (h != nullptr) { const ScopedPointer deleter (h); h = h->nextEntry; } hashSlots.set (i, nullptr); } totalNumItems = 0; } //============================================================================== /** Returns the current number of items in the map. */ inline int size() const noexcept { return totalNumItems; } /** Returns the value corresponding to a given key. If the map doesn't contain the key, a default instance of the value type is returned. @param keyToLookFor the key of the item being requested */ inline ValueType operator[] (KeyTypeParameter keyToLookFor) const { const ScopedLockType sl (getLock()); for (const HashEntry* entry = hashSlots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return entry->value; return ValueType(); } //============================================================================== /** Returns true if the map contains an item with the specied key. */ bool contains (KeyTypeParameter keyToLookFor) const { const ScopedLockType sl (getLock()); for (const HashEntry* entry = hashSlots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return true; return false; } /** Returns true if the hash contains at least one occurrence of a given value. */ bool containsValue (ValueTypeParameter valueToLookFor) const { const ScopedLockType sl (getLock()); for (int i = getNumSlots(); --i >= 0;) for (const HashEntry* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) if (entry->value == valueToLookFor) return true; return false; } //============================================================================== /** Adds or replaces an element in the hash-map. If there's already an item with the given key, this will replace its value. Otherwise, a new item will be added to the map. */ void set (KeyTypeParameter newKey, ValueTypeParameter newValue) { const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (newKey); HashEntry* const firstEntry = hashSlots.getUnchecked (hashIndex); for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) { if (entry->key == newKey) { entry->value = newValue; return; } } hashSlots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); ++totalNumItems; if (totalNumItems > (getNumSlots() * 3) / 2) remapTable (getNumSlots() * 2); } /** Removes an item with the given key. */ void remove (KeyTypeParameter keyToRemove) { const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (keyToRemove); HashEntry* entry = hashSlots.getUnchecked (hashIndex); HashEntry* previous = nullptr; while (entry != nullptr) { if (entry->key == keyToRemove) { const ScopedPointer deleter (entry); entry = entry->nextEntry; if (previous != nullptr) previous->nextEntry = entry; else hashSlots.set (hashIndex, entry); --totalNumItems; } else { previous = entry; entry = entry->nextEntry; } } } /** Removes all items with the given value. */ void removeValue (ValueTypeParameter valueToRemove) { const ScopedLockType sl (getLock()); for (int i = getNumSlots(); --i >= 0;) { HashEntry* entry = hashSlots.getUnchecked(i); HashEntry* previous = nullptr; while (entry != nullptr) { if (entry->value == valueToRemove) { const ScopedPointer deleter (entry); entry = entry->nextEntry; if (previous != nullptr) previous->nextEntry = entry; else hashSlots.set (i, entry); --totalNumItems; } else { previous = entry; entry = entry->nextEntry; } } } } /** Remaps the hash-map to use a different number of slots for its hash function. Each slot corresponds to a single hash-code, and each one can contain multiple items. @see getNumSlots() */ void remapTable (int newNumberOfSlots) { HashMap newTable (newNumberOfSlots); for (int i = getNumSlots(); --i >= 0;) for (const HashEntry* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) newTable.set (entry->key, entry->value); swapWith (newTable); } /** Returns the number of slots which are available for hashing. Each slot corresponds to a single hash-code, and each one can contain multiple items. @see getNumSlots() */ inline int getNumSlots() const noexcept { return hashSlots.size(); } //============================================================================== /** Efficiently swaps the contents of two hash-maps. */ template void swapWith (OtherHashMapType& otherHashMap) noexcept { const ScopedLockType lock1 (getLock()); const typename OtherHashMapType::ScopedLockType lock2 (otherHashMap.getLock()); hashSlots.swapWith (otherHashMap.hashSlots); std::swap (totalNumItems, otherHashMap.totalNumItems); } //============================================================================== /** Returns the CriticalSection that locks this structure. To lock, you can call getLock().enter() and getLock().exit(), or preferably use an object of ScopedLockType as an RAII lock for it. */ inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return lock; } /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; private: //============================================================================== class HashEntry { public: HashEntry (KeyTypeParameter k, ValueTypeParameter val, HashEntry* const next) : key (k), value (val), nextEntry (next) {} const KeyType key; ValueType value; HashEntry* nextEntry; JUCE_DECLARE_NON_COPYABLE (HashEntry) }; public: //============================================================================== /** Iterates over the items in a HashMap. To use it, repeatedly call next() until it returns false, e.g. @code HashMap myMap; HashMap::Iterator i (myMap); while (i.next()) { DBG (i.getKey() << " -> " << i.getValue()); } @endcode The order in which items are iterated bears no resemblence to the order in which they were originally added! Obviously as soon as you call any non-const methods on the original hash-map, any iterators that were created beforehand will cease to be valid, and should not be used. @see HashMap */ class Iterator { public: //============================================================================== Iterator (const HashMap& hashMapToIterate) : hashMap (hashMapToIterate), entry (nullptr), index (0) {} /** Moves to the next item, if one is available. When this returns true, you can get the item's key and value using getKey() and getValue(). If it returns false, the iteration has finished and you should stop. */ bool next() { if (entry != nullptr) entry = entry->nextEntry; while (entry == nullptr) { if (index >= hashMap.getNumSlots()) return false; entry = hashMap.hashSlots.getUnchecked (index++); } return true; } /** Returns the current item's key. This should only be called when a call to next() has just returned true. */ KeyType getKey() const { return entry != nullptr ? entry->key : KeyType(); } /** Returns the current item's value. This should only be called when a call to next() has just returned true. */ ValueType getValue() const { return entry != nullptr ? entry->value : ValueType(); } private: //============================================================================== const HashMap& hashMap; HashEntry* entry; int index; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator) }; private: //============================================================================== enum { defaultHashTableSize = 101 }; friend class Iterator; HashFunctionType hashFunctionToUse; Array hashSlots; int totalNumItems; TypeOfCriticalSectionToUse lock; int generateHashFor (KeyTypeParameter key) const { const int hash = hashFunctionToUse.generateHash (key, getNumSlots()); jassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers! return hash; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap) }; #endif // JUCE_HASHMAP_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h000066400000000000000000000302321320201440200324710ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_LINKEDLISTPOINTER_H_INCLUDED #define JUCE_LINKEDLISTPOINTER_H_INCLUDED //============================================================================== /** Helps to manipulate singly-linked lists of objects. For objects that are designed to contain a pointer to the subsequent item in the list, this class contains methods to deal with the list. To use it, the ObjectType class that it points to must contain a LinkedListPointer called nextListItem, e.g. @code struct MyObject { int x, y, z; // A linkable object must contain a member with this name and type, which must be // accessible by the LinkedListPointer class. (This doesn't mean it has to be public - // you could make your class a friend of a LinkedListPointer instead). LinkedListPointer nextListItem; }; LinkedListPointer myList; myList.append (new MyObject()); myList.append (new MyObject()); int numItems = myList.size(); // returns 2 MyObject* lastInList = myList.getLast(); @endcode */ template class LinkedListPointer { public: //============================================================================== /** Creates a null pointer to an empty list. */ LinkedListPointer() noexcept : item (nullptr) { } /** Creates a pointer to a list whose head is the item provided. */ explicit LinkedListPointer (ObjectType* const headItem) noexcept : item (headItem) { } /** Sets this pointer to point to a new list. */ LinkedListPointer& operator= (ObjectType* const newItem) noexcept { item = newItem; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS LinkedListPointer (LinkedListPointer&& other) noexcept : item (other.item) { other.item = nullptr; } LinkedListPointer& operator= (LinkedListPointer&& other) noexcept { jassert (this != &other); // hopefully the compiler should make this situation impossible! item = other.item; other.item = nullptr; return *this; } #endif //============================================================================== /** Returns the item which this pointer points to. */ inline operator ObjectType*() const noexcept { return item; } /** Returns the item which this pointer points to. */ inline ObjectType* get() const noexcept { return item; } /** Returns the last item in the list which this pointer points to. This will iterate the list and return the last item found. Obviously the speed of this operation will be proportional to the size of the list. If the list is empty the return value will be this object. If you're planning on appending a number of items to your list, it's much more efficient to use the Appender class than to repeatedly call getLast() to find the end. */ LinkedListPointer& getLast() noexcept { LinkedListPointer* l = this; while (l->item != nullptr) l = &(l->item->nextListItem); return *l; } /** Returns the number of items in the list. Obviously with a simple linked list, getting the size involves iterating the list, so this can be a lengthy operation - be careful when using this method in your code. */ int size() const noexcept { int total = 0; for (ObjectType* i = item; i != nullptr; i = i->nextListItem) ++total; return total; } /** Returns the item at a given index in the list. Since the only way to find an item is to iterate the list, this operation can obviously be slow, depending on its size, so you should be careful when using this in algorithms. */ LinkedListPointer& operator[] (int index) noexcept { LinkedListPointer* l = this; while (--index >= 0 && l->item != nullptr) l = &(l->item->nextListItem); return *l; } /** Returns the item at a given index in the list. Since the only way to find an item is to iterate the list, this operation can obviously be slow, depending on its size, so you should be careful when using this in algorithms. */ const LinkedListPointer& operator[] (int index) const noexcept { const LinkedListPointer* l = this; while (--index >= 0 && l->item != nullptr) l = &(l->item->nextListItem); return *l; } /** Returns true if the list contains the given item. */ bool contains (const ObjectType* const itemToLookFor) const noexcept { for (ObjectType* i = item; i != nullptr; i = i->nextListItem) if (itemToLookFor == i) return true; return false; } //============================================================================== /** Inserts an item into the list, placing it before the item that this pointer currently points to. */ void insertNext (ObjectType* const newItem) { jassert (newItem != nullptr); jassert (newItem->nextListItem == nullptr); newItem->nextListItem = item; item = newItem; } /** Inserts an item at a numeric index in the list. Obviously this will involve iterating the list to find the item at the given index, so be careful about the impact this may have on execution time. */ void insertAtIndex (int index, ObjectType* newItem) { jassert (newItem != nullptr); LinkedListPointer* l = this; while (index != 0 && l->item != nullptr) { l = &(l->item->nextListItem); --index; } l->insertNext (newItem); } /** Replaces the object that this pointer points to, appending the rest of the list to the new object, and returning the old one. */ ObjectType* replaceNext (ObjectType* const newItem) noexcept { jassert (newItem != nullptr); jassert (newItem->nextListItem == nullptr); ObjectType* const oldItem = item; item = newItem; item->nextListItem = oldItem->nextListItem.item; oldItem->nextListItem.item = nullptr; return oldItem; } /** Adds an item to the end of the list. This operation involves iterating the whole list, so can be slow - if you need to append a number of items to your list, it's much more efficient to use the Appender class than to repeatedly call append(). */ void append (ObjectType* const newItem) { getLast().item = newItem; } /** Creates copies of all the items in another list and adds them to this one. This will use the ObjectType's copy constructor to try to create copies of each item in the other list, and appends them to this list. */ void addCopyOfList (const LinkedListPointer& other) { LinkedListPointer* insertPoint = this; for (ObjectType* i = other.item; i != nullptr; i = i->nextListItem) { insertPoint->insertNext (new ObjectType (*i)); insertPoint = &(insertPoint->item->nextListItem); } } /** Removes the head item from the list. This won't delete the object that is removed, but returns it, so the caller can delete it if necessary. */ ObjectType* removeNext() noexcept { ObjectType* const oldItem = item; if (oldItem != nullptr) { item = oldItem->nextListItem; oldItem->nextListItem.item = nullptr; } return oldItem; } /** Removes a specific item from the list. Note that this will not delete the item, it simply unlinks it from the list. */ void remove (ObjectType* const itemToRemove) { if (LinkedListPointer* const l = findPointerTo (itemToRemove)) l->removeNext(); } /** Iterates the list, calling the delete operator on all of its elements and leaving this pointer empty. */ void deleteAll() { while (item != nullptr) { ObjectType* const oldItem = item; item = oldItem->nextListItem; delete oldItem; } } /** Finds a pointer to a given item. If the item is found in the list, this returns the pointer that points to it. If the item isn't found, this returns null. */ LinkedListPointer* findPointerTo (ObjectType* const itemToLookFor) noexcept { LinkedListPointer* l = this; while (l->item != nullptr) { if (l->item == itemToLookFor) return l; l = &(l->item->nextListItem); } return nullptr; } /** Copies the items in the list to an array. The destArray must contain enough elements to hold the entire list - no checks are made for this! */ void copyToArray (ObjectType** destArray) const noexcept { jassert (destArray != nullptr); for (ObjectType* i = item; i != nullptr; i = i->nextListItem) *destArray++ = i; } /** Swaps this pointer with another one */ void swapWith (LinkedListPointer& other) noexcept { std::swap (item, other.item); } //============================================================================== /** Allows efficient repeated insertions into a list. You can create an Appender object which points to the last element in your list, and then repeatedly call Appender::append() to add items to the end of the list in O(1) time. */ class Appender { public: /** Creates an appender which will add items to the given list. */ Appender (LinkedListPointer& endOfListPointer) noexcept : endOfList (&endOfListPointer) { // This can only be used to add to the end of a list. jassert (endOfListPointer.item == nullptr); } /** Appends an item to the list. */ void append (ObjectType* const newItem) noexcept { *endOfList = newItem; endOfList = &(newItem->nextListItem); } private: LinkedListPointer* endOfList; JUCE_DECLARE_NON_COPYABLE (Appender) }; private: //============================================================================== ObjectType* item; JUCE_DECLARE_NON_COPYABLE (LinkedListPointer) }; #endif // JUCE_LINKEDLISTPOINTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp000066400000000000000000000166051320201440200321260ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ struct NamedValueSet::NamedValue { NamedValue() noexcept {} NamedValue (const Identifier& n, const var& v) : name (n), value (v) {} NamedValue (const NamedValue& other) : name (other.name), value (other.value) {} #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS NamedValue (NamedValue&& other) noexcept : name (static_cast (other.name)), value (static_cast (other.value)) { } NamedValue (Identifier&& n, var&& v) : name (static_cast (n)), value (static_cast (v)) { } NamedValue& operator= (NamedValue&& other) noexcept { name = static_cast (other.name); value = static_cast (other.value); return *this; } #endif bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; } bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); } Identifier name; var value; }; //============================================================================== NamedValueSet::NamedValueSet() noexcept { } NamedValueSet::NamedValueSet (const NamedValueSet& other) : values (other.values) { } NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) { clear(); values = other.values; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept : values (static_cast &&> (other.values)) { } NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept { other.values.swapWith (values); return *this; } #endif NamedValueSet::~NamedValueSet() noexcept { } void NamedValueSet::clear() { values.clear(); } bool NamedValueSet::operator== (const NamedValueSet& other) const { return values == other.values; } bool NamedValueSet::operator!= (const NamedValueSet& other) const { return ! operator== (other); } int NamedValueSet::size() const noexcept { return values.size(); } const var& NamedValueSet::operator[] (const Identifier& name) const noexcept { if (const var* v = getVarPointer (name)) return *v; return var::null; } var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const { if (const var* const v = getVarPointer (name)) return *v; return defaultReturnValue; } var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept { for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) if (i->name == name) return &(i->value); return nullptr; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS bool NamedValueSet::set (const Identifier& name, var&& newValue) { if (var* const v = getVarPointer (name)) { if (v->equalsWithSameType (newValue)) return false; *v = static_cast (newValue); return true; } values.add (NamedValue (name, static_cast (newValue))); return true; } #endif bool NamedValueSet::set (const Identifier& name, const var& newValue) { if (var* const v = getVarPointer (name)) { if (v->equalsWithSameType (newValue)) return false; *v = newValue; return true; } values.add (NamedValue (name, newValue)); return true; } bool NamedValueSet::contains (const Identifier& name) const noexcept { return getVarPointer (name) != nullptr; } int NamedValueSet::indexOf (const Identifier& name) const noexcept { const int numValues = values.size(); for (int i = 0; i < numValues; ++i) if (values.getReference(i).name == name) return i; return -1; } bool NamedValueSet::remove (const Identifier& name) { const int numValues = values.size(); for (int i = 0; i < numValues; ++i) { if (values.getReference(i).name == name) { values.remove (i); return true; } } return false; } Identifier NamedValueSet::getName (const int index) const noexcept { if (isPositiveAndBelow (index, values.size())) return values.getReference (index).name; jassertfalse; return Identifier(); } const var& NamedValueSet::getValueAt (const int index) const noexcept { if (isPositiveAndBelow (index, values.size())) return values.getReference (index).value; jassertfalse; return var::null; } var* NamedValueSet::getVarPointerAt (int index) const noexcept { if (isPositiveAndBelow (index, values.size())) return &(values.getReference (index).value); return nullptr; } void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) { values.clearQuick(); for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem) { if (att->name.toString().startsWith ("base64:")) { MemoryBlock mb; if (mb.fromBase64Encoding (att->value)) { values.add (NamedValue (att->name.toString().substring (7), var (mb))); continue; } } values.add (NamedValue (att->name, var (att->value))); } } void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const { for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) { if (const MemoryBlock* mb = i->value.getBinaryData()) { xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding()); } else { // These types can't be stored as XML! jassert (! i->value.isObject()); jassert (! i->value.isMethod()); jassert (! i->value.isArray()); xml.setAttribute (i->name.toString(), i->value.toString()); } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h000066400000000000000000000130101320201440200315560ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_NAMEDVALUESET_H_INCLUDED #define JUCE_NAMEDVALUESET_H_INCLUDED //============================================================================== /** Holds a set of named var objects. This can be used as a basic structure to hold a set of var object, which can be retrieved by using their identifier. */ class JUCE_API NamedValueSet { public: /** Creates an empty set. */ NamedValueSet() noexcept; /** Creates a copy of another set. */ NamedValueSet (const NamedValueSet&); /** Replaces this set with a copy of another set. */ NamedValueSet& operator= (const NamedValueSet&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS NamedValueSet (NamedValueSet&&) noexcept; NamedValueSet& operator= (NamedValueSet&&) noexcept; #endif /** Destructor. */ ~NamedValueSet() noexcept; bool operator== (const NamedValueSet&) const; bool operator!= (const NamedValueSet&) const; //============================================================================== /** Returns the total number of values that the set contains. */ int size() const noexcept; /** Returns the value of a named item. If the name isn't found, this will return a void variant. @see getProperty */ const var& operator[] (const Identifier& name) const noexcept; /** Tries to return the named value, but if no such value is found, this will instead return the supplied default value. */ var getWithDefault (const Identifier& name, const var& defaultReturnValue) const; /** Changes or adds a named value. @returns true if a value was changed or added; false if the value was already set the value passed-in. */ bool set (const Identifier& name, const var& newValue); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS /** Changes or adds a named value. @returns true if a value was changed or added; false if the value was already set the value passed-in. */ bool set (const Identifier& name, var&& newValue); #endif /** Returns true if the set contains an item with the specified name. */ bool contains (const Identifier& name) const noexcept; /** Removes a value from the set. @returns true if a value was removed; false if there was no value with the name that was given. */ bool remove (const Identifier& name); /** Returns the name of the value at a given index. The index must be between 0 and size() - 1. */ Identifier getName (int index) const noexcept; /** Returns a pointer to the var that holds a named value, or null if there is no value with this name. Do not use this method unless you really need access to the internal var object for some reason - for normal reading and writing always prefer operator[]() and set(). */ var* getVarPointer (const Identifier& name) const noexcept; /** Returns the value of the item at a given index. The index must be between 0 and size() - 1. */ const var& getValueAt (int index) const noexcept; /** Returns the value of the item at a given index. The index must be between 0 and size() - 1, or this will return a nullptr */ var* getVarPointerAt (int index) const noexcept; /** Returns the index of the given name, or -1 if it's not found. */ int indexOf (const Identifier& name) const noexcept; /** Removes all values. */ void clear(); //============================================================================== /** Sets properties to the values of all of an XML element's attributes. */ void setFromXmlAttributes (const XmlElement& xml); /** Sets attributes in an XML element corresponding to each of this object's properties. */ void copyToXmlAttributes (XmlElement& xml) const; private: //============================================================================== struct NamedValue; Array values; }; #endif // JUCE_NAMEDVALUESET_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h000066400000000000000000001024221320201440200311420ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_OWNEDARRAY_H_INCLUDED #define JUCE_OWNEDARRAY_H_INCLUDED //============================================================================== /** An array designed for holding objects. This holds a list of pointers to objects, and will automatically delete the objects when they are removed from the array, or when the array is itself deleted. Declare it in the form: OwnedArray ..and then add new objects, e.g. myOwnedArray.add (new MyObjectClass()); After adding objects, they are 'owned' by the array and will be deleted when removed or replaced. To make all the array's methods thread-safe, pass in "CriticalSection" as the templated TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. @see Array, ReferenceCountedArray, StringArray, CriticalSection */ template class OwnedArray { public: //============================================================================== /** Creates an empty array. */ OwnedArray() noexcept : numUsed (0) { } /** Deletes the array and also deletes any objects inside it. To get rid of the array without deleting its objects, use its clear (false) method before deleting it. */ ~OwnedArray() { deleteAllObjects(); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS OwnedArray (OwnedArray&& other) noexcept : data (static_cast &&> (other.data)), numUsed (other.numUsed) { other.numUsed = 0; } OwnedArray& operator= (OwnedArray&& other) noexcept { const ScopedLockType lock (getLock()); deleteAllObjects(); data = static_cast &&> (other.data); numUsed = other.numUsed; other.numUsed = 0; return *this; } #endif //============================================================================== /** Clears the array, optionally deleting the objects inside it first. */ void clear (bool deleteObjects = true) { const ScopedLockType lock (getLock()); if (deleteObjects) deleteAllObjects(); data.setAllocatedSize (0); numUsed = 0; } //============================================================================== /** Clears the array, optionally deleting the objects inside it first. */ void clearQuick (bool deleteObjects) { const ScopedLockType lock (getLock()); if (deleteObjects) deleteAllObjects(); numUsed = 0; } //============================================================================== /** Returns the number of items currently in the array. @see operator[] */ inline int size() const noexcept { return numUsed; } /** Returns a pointer to the object at this index in the array. If the index is out-of-range, this will return a null pointer, (and it could be null anyway, because it's ok for the array to hold null pointers as well as objects). @see getUnchecked */ inline ObjectClass* operator[] (const int index) const noexcept { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (index, numUsed)) { jassert (data.elements != nullptr); return data.elements [index]; } return nullptr; } /** Returns a pointer to the object at this index in the array, without checking whether the index is in-range. This is a faster and less safe version of operator[] which doesn't check the index passed in, so it can be used when you're sure the index is always going to be legal. */ inline ObjectClass* getUnchecked (const int index) const noexcept { const ScopedLockType lock (getLock()); jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } /** Returns a pointer to the first object in the array. This will return a null pointer if the array's empty. @see getLast */ inline ObjectClass* getFirst() const noexcept { const ScopedLockType lock (getLock()); if (numUsed > 0) { jassert (data.elements != nullptr); return data.elements [0]; } return nullptr; } /** Returns a pointer to the last object in the array. This will return a null pointer if the array's empty. @see getFirst */ inline ObjectClass* getLast() const noexcept { const ScopedLockType lock (getLock()); if (numUsed > 0) { jassert (data.elements != nullptr); return data.elements [numUsed - 1]; } return nullptr; } /** Returns a pointer to the actual array data. This pointer will only be valid until the next time a non-const method is called on the array. */ inline ObjectClass** getRawDataPointer() noexcept { return data.elements; } //============================================================================== /** Returns a pointer to the first element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ObjectClass** begin() const noexcept { return data.elements; } /** Returns a pointer to the element which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ObjectClass** end() const noexcept { #if JUCE_DEBUG if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy) return data.elements; #endif return data.elements + numUsed; } //============================================================================== /** Finds the index of an object which might be in the array. @param objectToLookFor the object to look for @returns the index at which the object was found, or -1 if it's not found */ int indexOf (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); ObjectClass* const* e = data.elements.getData(); ObjectClass* const* const end_ = e + numUsed; for (; e != end_; ++e) if (objectToLookFor == *e) return static_cast (e - data.elements.getData()); return -1; } /** Returns true if the array contains a specified object. @param objectToLookFor the object to look for @returns true if the object is in the array */ bool contains (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); ObjectClass* const* e = data.elements.getData(); ObjectClass* const* const end_ = e + numUsed; for (; e != end_; ++e) if (objectToLookFor == *e) return true; return false; } //============================================================================== /** Appends a new object to the end of the array. Note that the this object will be deleted by the OwnedArray when it is removed, so be careful not to delete it somewhere else. Also be careful not to add the same object to the array more than once, as this will obviously cause deletion of dangling pointers. @param newObject the new object to add to the array @returns the new object that was added @see set, insert, addIfNotAlreadyThere, addSorted */ ObjectClass* add (ObjectClass* newObject) noexcept { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); jassert (data.elements != nullptr); data.elements [numUsed++] = newObject; return newObject; } /** Inserts a new object into the array at the given index. Note that the this object will be deleted by the OwnedArray when it is removed, so be careful not to delete it somewhere else. If the index is less than 0 or greater than the size of the array, the element will be added to the end of the array. Otherwise, it will be inserted into the array, moving all the later elements along to make room. Be careful not to add the same object to the array more than once, as this will obviously cause deletion of dangling pointers. @param indexToInsertAt the index at which the new element should be inserted @param newObject the new object to add to the array @returns the new object that was added @see add, addSorted, addIfNotAlreadyThere, set */ ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept { if (indexToInsertAt < 0) return add (newObject); const ScopedLockType lock (getLock()); if (indexToInsertAt > numUsed) indexToInsertAt = numUsed; data.ensureAllocatedSize (numUsed + 1); jassert (data.elements != nullptr); ObjectClass** const e = data.elements + indexToInsertAt; const int numToMove = numUsed - indexToInsertAt; if (numToMove > 0) memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); *e = newObject; ++numUsed; return newObject; } /** Inserts an array of values into this array at a given position. If the index is less than 0 or greater than the size of the array, the new elements will be added to the end of the array. Otherwise, they will be inserted into the array, moving all the later elements along to make room. @param indexToInsertAt the index at which the first new element should be inserted @param newObjects the new values to add to the array @param numberOfElements how many items are in the array @see insert, add, addSorted, set */ void insertArray (int indexToInsertAt, ObjectClass* const* newObjects, int numberOfElements) { if (numberOfElements > 0) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + numberOfElements); ObjectClass** insertPos = data.elements; if (isPositiveAndBelow (indexToInsertAt, numUsed)) { insertPos += indexToInsertAt; const size_t numberToMove = (size_t) (numUsed - indexToInsertAt); memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ObjectClass*)); } else { insertPos += numUsed; } numUsed += numberOfElements; while (--numberOfElements >= 0) *insertPos++ = *newObjects++; } } /** Appends a new object at the end of the array as long as the array doesn't already contain it. If the array already contains a matching object, nothing will be done. @param newObject the new object to add to the array @returns the new object that was added */ ObjectClass* addIfNotAlreadyThere (ObjectClass* newObject) noexcept { const ScopedLockType lock (getLock()); if (! contains (newObject)) add (newObject); return newObject; } /** Replaces an object in the array with a different one. If the index is less than zero, this method does nothing. If the index is beyond the end of the array, the new object is added to the end of the array. Be careful not to add the same object to the array more than once, as this will obviously cause deletion of dangling pointers. @param indexToChange the index whose value you want to change @param newObject the new value to set for this index. @param deleteOldElement whether to delete the object that's being replaced with the new one @see add, insert, remove */ ObjectClass* set (int indexToChange, ObjectClass* newObject, bool deleteOldElement = true) { if (indexToChange >= 0) { ScopedPointer toDelete; { const ScopedLockType lock (getLock()); if (indexToChange < numUsed) { if (deleteOldElement) { toDelete = data.elements [indexToChange]; if (toDelete == newObject) toDelete.release(); } data.elements [indexToChange] = newObject; } else { data.ensureAllocatedSize (numUsed + 1); data.elements [numUsed++] = newObject; } } } else { jassertfalse; // you're trying to set an object at a negative index, which doesn't have // any effect - but since the object is not being added, it may be leaking.. } return newObject; } /** Adds elements from another array to the end of this array. @param arrayToAddFrom the array from which to copy the elements @param startIndex the first element of the other array to start copying from @param numElementsToAdd how many elements to add from the other array. If this value is negative or greater than the number of available elements, all available elements will be copied. @see add */ template void addArray (const OtherArrayType& arrayToAddFrom, int startIndex = 0, int numElementsToAdd = -1) { const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock()); const ScopedLockType lock2 (getLock()); if (startIndex < 0) { jassertfalse; startIndex = 0; } if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) numElementsToAdd = arrayToAddFrom.size() - startIndex; data.ensureAllocatedSize (numUsed + numElementsToAdd); jassert (numElementsToAdd <= 0 || data.elements != nullptr); while (--numElementsToAdd >= 0) { data.elements [numUsed] = arrayToAddFrom.getUnchecked (startIndex++); ++numUsed; } } /** Adds copies of the elements in another array to the end of this array. The other array must be either an OwnedArray of a compatible type of object, or an Array containing pointers to the same kind of object. The objects involved must provide a copy constructor, and this will be used to create new copies of each element, and add them to this array. @param arrayToAddFrom the array from which to copy the elements @param startIndex the first element of the other array to start copying from @param numElementsToAdd how many elements to add from the other array. If this value is negative or greater than the number of available elements, all available elements will be copied. @see add */ template void addCopiesOf (const OtherArrayType& arrayToAddFrom, int startIndex = 0, int numElementsToAdd = -1) { const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock()); const ScopedLockType lock2 (getLock()); if (startIndex < 0) { jassertfalse; startIndex = 0; } if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) numElementsToAdd = arrayToAddFrom.size() - startIndex; data.ensureAllocatedSize (numUsed + numElementsToAdd); jassert (numElementsToAdd <= 0 || data.elements != nullptr); while (--numElementsToAdd >= 0) data.elements [numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++)); } /** Inserts a new object into the array assuming that the array is sorted. This will use a comparator to find the position at which the new object should go. If the array isn't sorted, the behaviour of this method will be unpredictable. @param comparator the comparator to use to compare the elements - see the sort method for details about this object's structure @param newObject the new object to insert to the array @returns the index at which the new object was added @see add, sort, indexOfSorted */ template int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept { (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed); insert (index, newObject); return index; } /** Finds the index of an object in the array, assuming that the array is sorted. This will use a comparator to do a binary-chop to find the index of the given element, if it exists. If the array isn't sorted, the behaviour of this method will be unpredictable. @param comparator the comparator to use to compare the elements - see the sort() method for details about the form this object should take @param objectToLookFor the object to search for @returns the index of the element, or -1 if it's not found @see addSorted, sort */ template int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept { (void) comparator; const ScopedLockType lock (getLock()); int s = 0, e = numUsed; while (s < e) { if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0) return s; const int halfway = (s + e) / 2; if (halfway == s) break; if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0) s = halfway; else e = halfway; } return -1; } //============================================================================== /** Removes an object from the array. This will remove the object at a given index (optionally also deleting it) and move back all the subsequent objects to close the gap. If the index passed in is out-of-range, nothing will happen. @param indexToRemove the index of the element to remove @param deleteObject whether to delete the object that is removed @see removeObject, removeRange */ void remove (int indexToRemove, bool deleteObject = true) { ScopedPointer toDelete; { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (indexToRemove, numUsed)) { ObjectClass** const e = data.elements + indexToRemove; if (deleteObject) toDelete = *e; --numUsed; const int numToShift = numUsed - indexToRemove; if (numToShift > 0) memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift); } } if ((numUsed << 1) < data.numAllocated) minimiseStorageOverheads(); } /** Removes and returns an object from the array without deleting it. This will remove the object at a given index and return it, moving back all the subsequent objects to close the gap. If the index passed in is out-of-range, nothing will happen. @param indexToRemove the index of the element to remove @see remove, removeObject, removeRange */ ObjectClass* removeAndReturn (int indexToRemove) { ObjectClass* removedItem = nullptr; const ScopedLockType lock (getLock()); if (isPositiveAndBelow (indexToRemove, numUsed)) { ObjectClass** const e = data.elements + indexToRemove; removedItem = *e; --numUsed; const int numToShift = numUsed - indexToRemove; if (numToShift > 0) memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift); if ((numUsed << 1) < data.numAllocated) minimiseStorageOverheads(); } return removedItem; } /** Removes a specified object from the array. If the item isn't found, no action is taken. @param objectToRemove the object to try to remove @param deleteObject whether to delete the object (if it's found) @see remove, removeRange */ void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true) { const ScopedLockType lock (getLock()); ObjectClass** const e = data.elements.getData(); for (int i = 0; i < numUsed; ++i) { if (objectToRemove == e[i]) { remove (i, deleteObject); break; } } } /** Removes a range of objects from the array. This will remove a set of objects, starting from the given index, and move any subsequent elements down to close the gap. If the range extends beyond the bounds of the array, it will be safely clipped to the size of the array. @param startIndex the index of the first object to remove @param numberToRemove how many objects should be removed @param deleteObjects whether to delete the objects that get removed @see remove, removeObject */ void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true) { const ScopedLockType lock (getLock()); const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); startIndex = jlimit (0, numUsed, startIndex); if (endIndex > startIndex) { if (deleteObjects) { for (int i = startIndex; i < endIndex; ++i) { ContainerDeletePolicy::destroy (data.elements [i]); data.elements [i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) } } const int rangeSize = endIndex - startIndex; ObjectClass** e = data.elements + startIndex; int numToShift = numUsed - endIndex; numUsed -= rangeSize; while (--numToShift >= 0) { *e = e [rangeSize]; ++e; } if ((numUsed << 1) < data.numAllocated) minimiseStorageOverheads(); } } /** Removes the last n objects from the array. @param howManyToRemove how many objects to remove from the end of the array @param deleteObjects whether to also delete the objects that are removed @see remove, removeObject, removeRange */ void removeLast (int howManyToRemove = 1, bool deleteObjects = true) { const ScopedLockType lock (getLock()); if (howManyToRemove >= numUsed) clear (deleteObjects); else removeRange (numUsed - howManyToRemove, howManyToRemove, deleteObjects); } /** Swaps a pair of objects in the array. If either of the indexes passed in is out-of-range, nothing will happen, otherwise the two objects at these positions will be exchanged. */ void swap (int index1, int index2) noexcept { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (index1, numUsed) && isPositiveAndBelow (index2, numUsed)) { std::swap (data.elements [index1], data.elements [index2]); } } /** Moves one of the objects to a different position. This will move the object to a specified index, shuffling along any intervening elements as required. So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. @param currentIndex the index of the object to be moved. If this isn't a valid index, then nothing will be done @param newIndex the index at which you'd like this object to end up. If this is less than zero, it will be moved to the end of the array */ void move (int currentIndex, int newIndex) noexcept { if (currentIndex != newIndex) { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (currentIndex, numUsed)) { if (! isPositiveAndBelow (newIndex, numUsed)) newIndex = numUsed - 1; ObjectClass* const value = data.elements [currentIndex]; if (newIndex > currentIndex) { memmove (data.elements + currentIndex, data.elements + currentIndex + 1, sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex)); } else { memmove (data.elements + newIndex + 1, data.elements + newIndex, sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex)); } data.elements [newIndex] = value; } } } /** This swaps the contents of this array with those of another array. If you need to exchange two arrays, this is vastly quicker than using copy-by-value because it just swaps their internal pointers. */ template void swapWith (OtherArrayType& otherArray) noexcept { const ScopedLockType lock1 (getLock()); const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); data.swapWith (otherArray.data); std::swap (numUsed, otherArray.numUsed); } //============================================================================== /** Reduces the amount of storage being used by the array. Arrays typically allocate slightly more storage than they need, and after removing elements, they may have quite a lot of unused space allocated. This method will reduce the amount of allocated storage to a minimum. */ void minimiseStorageOverheads() noexcept { const ScopedLockType lock (getLock()); data.shrinkToNoMoreThan (numUsed); } /** Increases the array's internal storage to hold a minimum number of elements. Calling this before adding a large known number of elements means that the array won't have to keep dynamically resizing itself as the elements are added, and it'll therefore be more efficient. */ void ensureStorageAllocated (const int minNumElements) noexcept { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (minNumElements); } //============================================================================== /** Sorts the elements in the array. This will use a comparator object to sort the elements into order. The object passed must have a method of the form: @code int compareElements (ElementType* first, ElementType* second); @endcode ..and this method must return: - a value of < 0 if the first comes before the second - a value of 0 if the two objects are equivalent - a value of > 0 if the second comes before the first To improve performance, the compareElements() method can be declared as static or const. @param comparator the comparator to use for comparing elements. @param retainOrderOfEquivalentItems if this is true, then items which the comparator says are equivalent will be kept in the order in which they currently appear in the array. This is slower to perform, but may be important in some cases. If it's false, a faster algorithm is used, but equivalent elements may be rearranged. @see sortArray, indexOfSorted */ template void sort (ElementComparator& comparator, bool retainOrderOfEquivalentItems = false) const noexcept { (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); } //============================================================================== /** Returns the CriticalSection that locks this array. To lock, you can call getLock().enter() and getLock().exit(), or preferably use an object of ScopedLockType as an RAII lock for it. */ inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; } /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; //============================================================================== #ifndef DOXYGEN // Note that the swapWithArray method has been replaced by a more flexible templated version, // and renamed "swapWith" to be more consistent with the names used in other classes. JUCE_DEPRECATED_WITH_BODY (void swapWithArray (OwnedArray& other) noexcept, { swapWith (other); }) #endif private: //============================================================================== ArrayAllocationBase data; int numUsed; void deleteAllObjects() { while (numUsed > 0) ContainerDeletePolicy::destroy (data.elements [--numUsed]); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray) }; #endif // JUCE_OWNEDARRAY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp000066400000000000000000000155011320201440200317230ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ PropertySet::PropertySet (const bool ignoreCaseOfKeyNames) : properties (ignoreCaseOfKeyNames), fallbackProperties (nullptr), ignoreCaseOfKeys (ignoreCaseOfKeyNames) { } PropertySet::PropertySet (const PropertySet& other) : properties (other.properties), fallbackProperties (other.fallbackProperties), ignoreCaseOfKeys (other.ignoreCaseOfKeys) { } PropertySet& PropertySet::operator= (const PropertySet& other) { properties = other.properties; fallbackProperties = other.fallbackProperties; ignoreCaseOfKeys = other.ignoreCaseOfKeys; propertyChanged(); return *this; } PropertySet::~PropertySet() { } void PropertySet::clear() { const ScopedLock sl (lock); if (properties.size() > 0) { properties.clear(); propertyChanged(); } } String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues() [index]; return fallbackProperties != nullptr ? fallbackProperties->getValue (keyName, defaultValue) : defaultValue; } int PropertySet::getIntValue (StringRef keyName, const int defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues() [index].getIntValue(); return fallbackProperties != nullptr ? fallbackProperties->getIntValue (keyName, defaultValue) : defaultValue; } double PropertySet::getDoubleValue (StringRef keyName, const double defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues()[index].getDoubleValue(); return fallbackProperties != nullptr ? fallbackProperties->getDoubleValue (keyName, defaultValue) : defaultValue; } bool PropertySet::getBoolValue (StringRef keyName, const bool defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues() [index].getIntValue() != 0; return fallbackProperties != nullptr ? fallbackProperties->getBoolValue (keyName, defaultValue) : defaultValue; } XmlElement* PropertySet::getXmlValue (StringRef keyName) const { return XmlDocument::parse (getValue (keyName)); } void PropertySet::setValue (const String& keyName, const var& v) { jassert (keyName.isNotEmpty()); // shouldn't use an empty key name! if (keyName.isNotEmpty()) { const String value (v.toString()); const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index < 0 || properties.getAllValues() [index] != value) { properties.set (keyName, value); propertyChanged(); } } } void PropertySet::removeValue (StringRef keyName) { if (keyName.isNotEmpty()) { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) { properties.remove (keyName); propertyChanged(); } } } void PropertySet::setValue (const String& keyName, const XmlElement* const xml) { setValue (keyName, xml == nullptr ? var() : var (xml->createDocument ("", true))); } bool PropertySet::containsKey (StringRef keyName) const noexcept { const ScopedLock sl (lock); return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys); } void PropertySet::addAllPropertiesFrom (const PropertySet& source) { const ScopedLock sl (source.getLock()); for (int i = 0; i < source.properties.size(); ++i) setValue (source.properties.getAllKeys() [i], source.properties.getAllValues() [i]); } void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noexcept { const ScopedLock sl (lock); fallbackProperties = fallbackProperties_; } XmlElement* PropertySet::createXml (const String& nodeName) const { const ScopedLock sl (lock); XmlElement* const xml = new XmlElement (nodeName); for (int i = 0; i < properties.getAllKeys().size(); ++i) { XmlElement* const e = xml->createNewChildElement ("VALUE"); e->setAttribute ("name", properties.getAllKeys()[i]); e->setAttribute ("val", properties.getAllValues()[i]); } return xml; } void PropertySet::restoreFromXml (const XmlElement& xml) { const ScopedLock sl (lock); clear(); forEachXmlChildElementWithTagName (xml, e, "VALUE") { if (e->hasAttribute ("name") && e->hasAttribute ("val")) { properties.set (e->getStringAttribute ("name"), e->getStringAttribute ("val")); } } if (properties.size() > 0) propertyChanged(); } void PropertySet::propertyChanged() { } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h000066400000000000000000000226271320201440200313770ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_PROPERTYSET_H_INCLUDED #define JUCE_PROPERTYSET_H_INCLUDED //============================================================================== /** A set of named property values, which can be strings, integers, floating point, etc. Effectively, this just wraps a StringPairArray in an interface that makes it easier to load and save types other than strings. See the PropertiesFile class for a subclass of this, which automatically broadcasts change messages and saves/loads the list from a file. */ class JUCE_API PropertySet { public: //============================================================================== /** Creates an empty PropertySet. @param ignoreCaseOfKeyNames if true, the names of properties are compared in a case-insensitive way */ PropertySet (bool ignoreCaseOfKeyNames = false); /** Creates a copy of another PropertySet. */ PropertySet (const PropertySet& other); /** Copies another PropertySet over this one. */ PropertySet& operator= (const PropertySet& other); /** Destructor. */ virtual ~PropertySet(); //============================================================================== /** Returns one of the properties as a string. If the value isn't found in this set, then this will look for it in a fallback property set (if you've specified one with the setFallbackPropertySet() method), and if it can't find one there, it'll return the default value passed-in. @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ String getValue (StringRef keyName, const String& defaultReturnValue = String()) const noexcept; /** Returns one of the properties as an integer. If the value isn't found in this set, then this will look for it in a fallback property set (if you've specified one with the setFallbackPropertySet() method), and if it can't find one there, it'll return the default value passed-in. @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ int getIntValue (StringRef keyName, int defaultReturnValue = 0) const noexcept; /** Returns one of the properties as an double. If the value isn't found in this set, then this will look for it in a fallback property set (if you've specified one with the setFallbackPropertySet() method), and if it can't find one there, it'll return the default value passed-in. @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ double getDoubleValue (StringRef keyName, double defaultReturnValue = 0.0) const noexcept; /** Returns one of the properties as an boolean. The result will be true if the string found for this key name can be parsed as a non-zero integer. If the value isn't found in this set, then this will look for it in a fallback property set (if you've specified one with the setFallbackPropertySet() method), and if it can't find one there, it'll return the default value passed-in. @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ bool getBoolValue (StringRef keyName, bool defaultReturnValue = false) const noexcept; /** Returns one of the properties as an XML element. The result will a new XMLElement object that the caller must delete. If may return nullptr if the key isn't found, or if the entry contains an string that isn't valid XML. If the value isn't found in this set, then this will look for it in a fallback property set (if you've specified one with the setFallbackPropertySet() method), and if it can't find one there, it'll return the default value passed-in. @param keyName the name of the property to retrieve */ XmlElement* getXmlValue (StringRef keyName) const; //============================================================================== /** Sets a named property. @param keyName the name of the property to set. (This mustn't be an empty string) @param value the new value to set it to */ void setValue (const String& keyName, const var& value); /** Sets a named property to an XML element. @param keyName the name of the property to set. (This mustn't be an empty string) @param xml the new element to set it to. If this is zero, the value will be set to an empty string @see getXmlValue */ void setValue (const String& keyName, const XmlElement* xml); /** This copies all the values from a source PropertySet to this one. This won't remove any existing settings, it just adds any that it finds in the source set. */ void addAllPropertiesFrom (const PropertySet& source); //============================================================================== /** Deletes a property. @param keyName the name of the property to delete. (This mustn't be an empty string) */ void removeValue (StringRef keyName); /** Returns true if the properies include the given key. */ bool containsKey (StringRef keyName) const noexcept; /** Removes all values. */ void clear(); //============================================================================== /** Returns the keys/value pair array containing all the properties. */ StringPairArray& getAllProperties() noexcept { return properties; } /** Returns the lock used when reading or writing to this set */ const CriticalSection& getLock() const noexcept { return lock; } //============================================================================== /** Returns an XML element which encapsulates all the items in this property set. The string parameter is the tag name that should be used for the node. @see restoreFromXml */ XmlElement* createXml (const String& nodeName) const; /** Reloads a set of properties that were previously stored as XML. The node passed in must have been created by the createXml() method. @see createXml */ void restoreFromXml (const XmlElement& xml); //============================================================================== /** Sets up a second PopertySet that will be used to look up any values that aren't set in this one. If you set this up to be a pointer to a second property set, then whenever one of the getValue() methods fails to find an entry in this set, it will look up that value in the fallback set, and if it finds it, it will return that. Make sure that you don't delete the fallback set while it's still being used by another set! To remove the fallback set, just call this method with a null pointer. @see getFallbackPropertySet */ void setFallbackPropertySet (PropertySet* fallbackProperties) noexcept; /** Returns the fallback property set. @see setFallbackPropertySet */ PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; } protected: /** Subclasses can override this to be told when one of the properies has been changed. */ virtual void propertyChanged(); private: StringPairArray properties; PropertySet* fallbackProperties; CriticalSection lock; bool ignoreCaseOfKeys; JUCE_LEAK_DETECTOR (PropertySet) }; #endif // JUCE_PROPERTYSET_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h000066400000000000000000001020311320201440200333020ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED #define JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED //============================================================================== /** Holds a list of objects derived from ReferenceCountedObject, or which implement basic reference-count handling methods. The template parameter specifies the class of the object you want to point to - the easiest way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods should behave. A ReferenceCountedArray holds objects derived from ReferenceCountedObject, and takes care of incrementing and decrementing their ref counts when they are added and removed from the array. To make all the array's methods thread-safe, pass in "CriticalSection" as the templated TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. @see Array, OwnedArray, StringArray */ template class ReferenceCountedArray { public: typedef ReferenceCountedObjectPtr ObjectClassPtr; //============================================================================== /** Creates an empty array. @see ReferenceCountedObject, Array, OwnedArray */ ReferenceCountedArray() noexcept : numUsed (0) { } /** Creates a copy of another array */ ReferenceCountedArray (const ReferenceCountedArray& other) noexcept { const ScopedLockType lock (other.getLock()); numUsed = other.size(); data.setAllocatedSize (numUsed); memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*)); for (int i = numUsed; --i >= 0;) if (ObjectClass* o = data.elements[i]) o->incReferenceCount(); } /** Creates a copy of another array */ template ReferenceCountedArray (const ReferenceCountedArray& other) noexcept { const typename ReferenceCountedArray::ScopedLockType lock (other.getLock()); numUsed = other.size(); data.setAllocatedSize (numUsed); memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*)); for (int i = numUsed; --i >= 0;) if (ObjectClass* o = data.elements[i]) o->incReferenceCount(); } /** Copies another array into this one. Any existing objects in this array will first be released. */ ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept { ReferenceCountedArray otherCopy (other); swapWith (otherCopy); return *this; } /** Copies another array into this one. Any existing objects in this array will first be released. */ template ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept { ReferenceCountedArray otherCopy (other); swapWith (otherCopy); return *this; } /** Destructor. Any objects in the array will be released, and may be deleted if not referenced from elsewhere. */ ~ReferenceCountedArray() { releaseAllObjects(); } //============================================================================== /** Removes all objects from the array. Any objects in the array that whose reference counts drop to zero will be deleted. */ void clear() { const ScopedLockType lock (getLock()); releaseAllObjects(); data.setAllocatedSize (0); } /** Removes all objects from the array without freeing the array's allocated storage. Any objects in the array that whose reference counts drop to zero will be deleted. @see clear */ void clearQuick() { const ScopedLockType lock (getLock()); releaseAllObjects(); } /** Returns the current number of objects in the array. */ inline int size() const noexcept { return numUsed; } /** Returns a pointer to the object at this index in the array. If the index is out-of-range, this will return a null pointer, (and it could be null anyway, because it's ok for the array to hold null pointers as well as objects). @see getUnchecked */ inline ObjectClassPtr operator[] (const int index) const noexcept { return getObjectPointer (index); } /** Returns a pointer to the object at this index in the array, without checking whether the index is in-range. This is a faster and less safe version of operator[] which doesn't check the index passed in, so it can be used when you're sure the index is always going to be legal. */ inline ObjectClassPtr getUnchecked (const int index) const noexcept { return getObjectPointerUnchecked (index); } /** Returns a raw pointer to the object at this index in the array. If the index is out-of-range, this will return a null pointer, (and it could be null anyway, because it's ok for the array to hold null pointers as well as objects). @see getUnchecked */ inline ObjectClass* getObjectPointer (const int index) const noexcept { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (index, numUsed)) { jassert (data.elements != nullptr); return data.elements [index]; } return ObjectClassPtr(); } /** Returns a raw pointer to the object at this index in the array, without checking whether the index is in-range. */ inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept { const ScopedLockType lock (getLock()); jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } /** Returns a pointer to the first object in the array. This will return a null pointer if the array's empty. @see getLast */ inline ObjectClassPtr getFirst() const noexcept { const ScopedLockType lock (getLock()); if (numUsed > 0) { jassert (data.elements != nullptr); return data.elements [0]; } return ObjectClassPtr(); } /** Returns a pointer to the last object in the array. This will return a null pointer if the array's empty. @see getFirst */ inline ObjectClassPtr getLast() const noexcept { const ScopedLockType lock (getLock()); if (numUsed > 0) { jassert (data.elements != nullptr); return data.elements [numUsed - 1]; } return ObjectClassPtr(); } /** Returns a pointer to the actual array data. This pointer will only be valid until the next time a non-const method is called on the array. */ inline ObjectClass** getRawDataPointer() const noexcept { return data.elements; } //============================================================================== /** Returns a pointer to the first element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ObjectClass** begin() const noexcept { return data.elements; } /** Returns a pointer to the element which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ObjectClass** end() const noexcept { return data.elements + numUsed; } //============================================================================== /** Finds the index of the first occurrence of an object in the array. @param objectToLookFor the object to look for @returns the index at which the object was found, or -1 if it's not found */ int indexOf (const ObjectClass* const objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); ObjectClass** e = data.elements.getData(); ObjectClass** const endPointer = e + numUsed; while (e != endPointer) { if (objectToLookFor == *e) return static_cast (e - data.elements.getData()); ++e; } return -1; } /** Returns true if the array contains a specified object. @param objectToLookFor the object to look for @returns true if the object is in the array */ bool contains (const ObjectClass* const objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); ObjectClass** e = data.elements.getData(); ObjectClass** const endPointer = e + numUsed; while (e != endPointer) { if (objectToLookFor == *e) return true; ++e; } return false; } /** Appends a new object to the end of the array. This will increase the new object's reference count. @param newObject the new object to add to the array @see set, insert, addIfNotAlreadyThere, addSorted, addArray */ ObjectClass* add (ObjectClass* const newObject) noexcept { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); jassert (data.elements != nullptr); data.elements [numUsed++] = newObject; if (newObject != nullptr) newObject->incReferenceCount(); return newObject; } /** Inserts a new object into the array at the given index. If the index is less than 0 or greater than the size of the array, the element will be added to the end of the array. Otherwise, it will be inserted into the array, moving all the later elements along to make room. This will increase the new object's reference count. @param indexToInsertAt the index at which the new element should be inserted @param newObject the new object to add to the array @see add, addSorted, addIfNotAlreadyThere, set */ ObjectClass* insert (int indexToInsertAt, ObjectClass* const newObject) noexcept { if (indexToInsertAt < 0) return add (newObject); const ScopedLockType lock (getLock()); if (indexToInsertAt > numUsed) indexToInsertAt = numUsed; data.ensureAllocatedSize (numUsed + 1); jassert (data.elements != nullptr); ObjectClass** const e = data.elements + indexToInsertAt; const int numToMove = numUsed - indexToInsertAt; if (numToMove > 0) memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); *e = newObject; if (newObject != nullptr) newObject->incReferenceCount(); ++numUsed; return newObject; } /** Appends a new object at the end of the array as long as the array doesn't already contain it. If the array already contains a matching object, nothing will be done. @param newObject the new object to add to the array */ void addIfNotAlreadyThere (ObjectClass* const newObject) noexcept { const ScopedLockType lock (getLock()); if (! contains (newObject)) add (newObject); } /** Replaces an object in the array with a different one. If the index is less than zero, this method does nothing. If the index is beyond the end of the array, the new object is added to the end of the array. The object being added has its reference count increased, and if it's replacing another object, then that one has its reference count decreased, and may be deleted. @param indexToChange the index whose value you want to change @param newObject the new value to set for this index. @see add, insert, remove */ void set (const int indexToChange, ObjectClass* const newObject) { if (indexToChange >= 0) { const ScopedLockType lock (getLock()); if (newObject != nullptr) newObject->incReferenceCount(); if (indexToChange < numUsed) { if (ObjectClass* o = data.elements [indexToChange]) releaseObject (o); data.elements [indexToChange] = newObject; } else { data.ensureAllocatedSize (numUsed + 1); jassert (data.elements != nullptr); data.elements [numUsed++] = newObject; } } } /** Adds elements from another array to the end of this array. @param arrayToAddFrom the array from which to copy the elements @param startIndex the first element of the other array to start copying from @param numElementsToAdd how many elements to add from the other array. If this value is negative or greater than the number of available elements, all available elements will be copied. @see add */ void addArray (const ReferenceCountedArray& arrayToAddFrom, int startIndex = 0, int numElementsToAdd = -1) noexcept { const ScopedLockType lock1 (arrayToAddFrom.getLock()); { const ScopedLockType lock2 (getLock()); if (startIndex < 0) { jassertfalse; startIndex = 0; } if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) numElementsToAdd = arrayToAddFrom.size() - startIndex; if (numElementsToAdd > 0) { data.ensureAllocatedSize (numUsed + numElementsToAdd); while (--numElementsToAdd >= 0) add (arrayToAddFrom.getUnchecked (startIndex++)); } } } /** Inserts a new object into the array assuming that the array is sorted. This will use a comparator to find the position at which the new object should go. If the array isn't sorted, the behaviour of this method will be unpredictable. @param comparator the comparator object to use to compare the elements - see the sort() method for details about this object's form @param newObject the new object to insert to the array @returns the index at which the new object was added @see add, sort */ template int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept { const ScopedLockType lock (getLock()); const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed); insert (index, newObject); return index; } /** Inserts or replaces an object in the array, assuming it is sorted. This is similar to addSorted, but if a matching element already exists, then it will be replaced by the new one, rather than the new one being added as well. */ template void addOrReplaceSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept { const ScopedLockType lock (getLock()); const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed); if (index > 0 && comparator.compareElements (newObject, data.elements [index - 1]) == 0) set (index - 1, newObject); // replace an existing object that matches else insert (index, newObject); // no match, so insert the new one } /** Finds the index of an object in the array, assuming that the array is sorted. This will use a comparator to do a binary-chop to find the index of the given element, if it exists. If the array isn't sorted, the behaviour of this method will be unpredictable. @param comparator the comparator to use to compare the elements - see the sort() method for details about the form this object should take @param objectToLookFor the object to search for @returns the index of the element, or -1 if it's not found @see addSorted, sort */ template int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept { (void) comparator; const ScopedLockType lock (getLock()); int s = 0, e = numUsed; while (s < e) { if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0) return s; const int halfway = (s + e) / 2; if (halfway == s) break; if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0) s = halfway; else e = halfway; } return -1; } //============================================================================== /** Removes an object from the array. This will remove the object at a given index and move back all the subsequent objects to close the gap. If the index passed in is out-of-range, nothing will happen. The object that is removed will have its reference count decreased, and may be deleted if not referenced from elsewhere. @param indexToRemove the index of the element to remove @see removeObject, removeRange */ void remove (const int indexToRemove) { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (indexToRemove, numUsed)) { ObjectClass** const e = data.elements + indexToRemove; if (ObjectClass* o = *e) releaseObject (o); --numUsed; const int numberToShift = numUsed - indexToRemove; if (numberToShift > 0) memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift); if ((numUsed << 1) < data.numAllocated) minimiseStorageOverheads(); } } /** Removes and returns an object from the array. This will remove the object at a given index and return it, moving back all the subsequent objects to close the gap. If the index passed in is out-of-range, nothing will happen and a null pointer will be returned. @param indexToRemove the index of the element to remove @see remove, removeObject, removeRange */ ObjectClassPtr removeAndReturn (const int indexToRemove) { ObjectClassPtr removedItem; const ScopedLockType lock (getLock()); if (isPositiveAndBelow (indexToRemove, numUsed)) { ObjectClass** const e = data.elements + indexToRemove; if (ObjectClass* o = *e) { removedItem = o; releaseObject (o); } --numUsed; const int numberToShift = numUsed - indexToRemove; if (numberToShift > 0) memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift); if ((numUsed << 1) < data.numAllocated) minimiseStorageOverheads(); } return removedItem; } /** Removes the first occurrence of a specified object from the array. If the item isn't found, no action is taken. If it is found, it is removed and has its reference count decreased. @param objectToRemove the object to try to remove @see remove, removeRange */ void removeObject (ObjectClass* const objectToRemove) { const ScopedLockType lock (getLock()); remove (indexOf (objectToRemove)); } /** Removes a range of objects from the array. This will remove a set of objects, starting from the given index, and move any subsequent elements down to close the gap. If the range extends beyond the bounds of the array, it will be safely clipped to the size of the array. The objects that are removed will have their reference counts decreased, and may be deleted if not referenced from elsewhere. @param startIndex the index of the first object to remove @param numberToRemove how many objects should be removed @see remove, removeObject */ void removeRange (const int startIndex, const int numberToRemove) { const ScopedLockType lock (getLock()); const int start = jlimit (0, numUsed, startIndex); const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); if (endIndex > start) { int i; for (i = start; i < endIndex; ++i) { if (ObjectClass* o = data.elements[i]) { releaseObject (o); data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) } } const int rangeSize = endIndex - start; ObjectClass** e = data.elements + start; i = numUsed - endIndex; numUsed -= rangeSize; while (--i >= 0) { *e = e [rangeSize]; ++e; } if ((numUsed << 1) < data.numAllocated) minimiseStorageOverheads(); } } /** Removes the last n objects from the array. The objects that are removed will have their reference counts decreased, and may be deleted if not referenced from elsewhere. @param howManyToRemove how many objects to remove from the end of the array @see remove, removeObject, removeRange */ void removeLast (int howManyToRemove = 1) { const ScopedLockType lock (getLock()); if (howManyToRemove > numUsed) howManyToRemove = numUsed; while (--howManyToRemove >= 0) remove (numUsed - 1); } /** Swaps a pair of objects in the array. If either of the indexes passed in is out-of-range, nothing will happen, otherwise the two objects at these positions will be exchanged. */ void swap (const int index1, const int index2) noexcept { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (index1, numUsed) && isPositiveAndBelow (index2, numUsed)) { std::swap (data.elements [index1], data.elements [index2]); } } /** Moves one of the objects to a different position. This will move the object to a specified index, shuffling along any intervening elements as required. So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. @param currentIndex the index of the object to be moved. If this isn't a valid index, then nothing will be done @param newIndex the index at which you'd like this object to end up. If this is less than zero, it will be moved to the end of the array */ void move (const int currentIndex, int newIndex) noexcept { if (currentIndex != newIndex) { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (currentIndex, numUsed)) { if (! isPositiveAndBelow (newIndex, numUsed)) newIndex = numUsed - 1; ObjectClass* const value = data.elements [currentIndex]; if (newIndex > currentIndex) { memmove (data.elements + currentIndex, data.elements + currentIndex + 1, sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex)); } else { memmove (data.elements + newIndex + 1, data.elements + newIndex, sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex)); } data.elements [newIndex] = value; } } } //============================================================================== /** This swaps the contents of this array with those of another array. If you need to exchange two arrays, this is vastly quicker than using copy-by-value because it just swaps their internal pointers. */ template void swapWith (OtherArrayType& otherArray) noexcept { const ScopedLockType lock1 (getLock()); const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); data.swapWith (otherArray.data); std::swap (numUsed, otherArray.numUsed); } //============================================================================== /** Compares this array to another one. @returns true only if the other array contains the same objects in the same order */ bool operator== (const ReferenceCountedArray& other) const noexcept { const ScopedLockType lock2 (other.getLock()); const ScopedLockType lock1 (getLock()); if (numUsed != other.numUsed) return false; for (int i = numUsed; --i >= 0;) if (data.elements [i] != other.data.elements [i]) return false; return true; } /** Compares this array to another one. @see operator== */ bool operator!= (const ReferenceCountedArray& other) const noexcept { return ! operator== (other); } //============================================================================== /** Sorts the elements in the array. This will use a comparator object to sort the elements into order. The object passed must have a method of the form: @code int compareElements (ElementType first, ElementType second); @endcode ..and this method must return: - a value of < 0 if the first comes before the second - a value of 0 if the two objects are equivalent - a value of > 0 if the second comes before the first To improve performance, the compareElements() method can be declared as static or const. @param comparator the comparator to use for comparing elements. @param retainOrderOfEquivalentItems if this is true, then items which the comparator says are equivalent will be kept in the order in which they currently appear in the array. This is slower to perform, but may be important in some cases. If it's false, a faster algorithm is used, but equivalent elements may be rearranged. @see sortArray */ template void sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false) const noexcept { (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); } //============================================================================== /** Reduces the amount of storage being used by the array. Arrays typically allocate slightly more storage than they need, and after removing elements, they may have quite a lot of unused space allocated. This method will reduce the amount of allocated storage to a minimum. */ void minimiseStorageOverheads() noexcept { const ScopedLockType lock (getLock()); data.shrinkToNoMoreThan (numUsed); } /** Increases the array's internal storage to hold a minimum number of elements. Calling this before adding a large known number of elements means that the array won't have to keep dynamically resizing itself as the elements are added, and it'll therefore be more efficient. */ void ensureStorageAllocated (const int minNumElements) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (minNumElements); } //============================================================================== /** Returns the CriticalSection that locks this array. To lock, you can call getLock().enter() and getLock().exit(), or preferably use an object of ScopedLockType as an RAII lock for it. */ inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; } /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; //============================================================================== #ifndef DOXYGEN // Note that the swapWithArray method has been replaced by a more flexible templated version, // and renamed "swapWith" to be more consistent with the names used in other classes. JUCE_DEPRECATED_WITH_BODY (void swapWithArray (ReferenceCountedArray& other) noexcept, { swapWith (other); }) #endif private: //============================================================================== ArrayAllocationBase data; int numUsed; void releaseAllObjects() { while (numUsed > 0) if (ObjectClass* o = data.elements [--numUsed]) releaseObject (o); jassert (numUsed == 0); } static void releaseObject (ObjectClass* o) { if (o->decReferenceCountWithoutDeleting()) ContainerDeletePolicy::destroy (o); } }; #endif // JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h000066400000000000000000000062611320201440200324740ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SCOPEDVALUESETTER_H_INCLUDED #define JUCE_SCOPEDVALUESETTER_H_INCLUDED //============================================================================== /** Helper class providing an RAII-based mechanism for temporarily setting and then re-setting a value. E.g. @code int x = 1; { ScopedValueSetter setter (x, 2); // x is now 2 } // x is now 1 again { ScopedValueSetter setter (x, 3, 4); // x is now 3 } // x is now 4 @endcode */ template class ScopedValueSetter { public: /** Creates a ScopedValueSetter that will immediately change the specified value to the given new value, and will then reset it to its original value when this object is deleted. */ ScopedValueSetter (ValueType& valueToSet, ValueType newValue) : value (valueToSet), originalValue (valueToSet) { valueToSet = newValue; } /** Creates a ScopedValueSetter that will immediately change the specified value to the given new value, and will then reset it to be valueWhenDeleted when this object is deleted. */ ScopedValueSetter (ValueType& valueToSet, ValueType newValue, ValueType valueWhenDeleted) : value (valueToSet), originalValue (valueWhenDeleted) { valueToSet = newValue; } ~ScopedValueSetter() { value = originalValue; } private: //============================================================================== ValueType& value; const ValueType originalValue; JUCE_DECLARE_NON_COPYABLE (ScopedValueSetter) }; #endif // JUCE_SCOPEDVALUESETTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h000066400000000000000000000423271320201440200310120ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SORTEDSET_H_INCLUDED #define JUCE_SORTEDSET_H_INCLUDED #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4512) #endif //============================================================================== /** Holds a set of unique primitive objects, such as ints or doubles. A set can only hold one item with a given value, so if for example it's a set of integers, attempting to add the same integer twice will do nothing the second time. Internally, the list of items is kept sorted (which means that whatever kind of primitive type is used must support the ==, <, >, <= and >= operators to determine the order), and searching the set for known values is very fast because it uses a binary-chop method. Note that if you're using a class or struct as the element type, it must be capable of being copied or moved with a straightforward memcpy, rather than needing construction and destruction code. To make all the set's methods thread-safe, pass in "CriticalSection" as the templated TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. @see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection */ template class SortedSet { public: //============================================================================== /** Creates an empty set. */ SortedSet() noexcept { } /** Creates a copy of another set. @param other the set to copy */ SortedSet (const SortedSet& other) : data (other.data) { } /** Destructor. */ ~SortedSet() noexcept { } /** Copies another set over this one. @param other the set to copy */ SortedSet& operator= (const SortedSet& other) noexcept { data = other.data; return *this; } //============================================================================== /** Compares this set to another one. Two sets are considered equal if they both contain the same set of elements. @param other the other set to compare with */ bool operator== (const SortedSet& other) const noexcept { return data == other.data; } /** Compares this set to another one. Two sets are considered equal if they both contain the same set of elements. @param other the other set to compare with */ bool operator!= (const SortedSet& other) const noexcept { return ! operator== (other); } //============================================================================== /** Removes all elements from the set. This will remove all the elements, and free any storage that the set is using. To clear it without freeing the storage, use the clearQuick() method instead. @see clearQuick */ void clear() noexcept { data.clear(); } /** Removes all elements from the set without freeing the array's allocated storage. @see clear */ void clearQuick() noexcept { data.clearQuick(); } //============================================================================== /** Returns the current number of elements in the set. */ inline int size() const noexcept { return data.size(); } /** Returns one of the elements in the set. If the index passed in is beyond the range of valid elements, this will return zero. If you're certain that the index will always be a valid element, you can call getUnchecked() instead, which is faster. @param index the index of the element being requested (0 is the first element in the set) @see getUnchecked, getFirst, getLast */ inline ElementType operator[] (const int index) const noexcept { return data [index]; } /** Returns one of the elements in the set, without checking the index passed in. Unlike the operator[] method, this will try to return an element without checking that the index is within the bounds of the set, so should only be used when you're confident that it will always be a valid index. @param index the index of the element being requested (0 is the first element in the set) @see operator[], getFirst, getLast */ inline ElementType getUnchecked (const int index) const noexcept { return data.getUnchecked (index); } /** Returns a direct reference to one of the elements in the set, without checking the index passed in. This is like getUnchecked, but returns a direct reference to the element, so that you can alter it directly. Obviously this can be dangerous, so only use it when absolutely necessary. @param index the index of the element being requested (0 is the first element in the array) */ inline ElementType& getReference (const int index) const noexcept { return data.getReference (index); } /** Returns the first element in the set, or 0 if the set is empty. @see operator[], getUnchecked, getLast */ inline ElementType getFirst() const noexcept { return data.getFirst(); } /** Returns the last element in the set, or 0 if the set is empty. @see operator[], getUnchecked, getFirst */ inline ElementType getLast() const noexcept { return data.getLast(); } //============================================================================== /** Returns a pointer to the first element in the set. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ElementType* begin() const noexcept { return data.begin(); } /** Returns a pointer to the element which follows the last element in the set. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline ElementType* end() const noexcept { return data.end(); } //============================================================================== /** Finds the index of the first element which matches the value passed in. This will search the set for the given object, and return the index of its first occurrence. If the object isn't found, the method will return -1. @param elementToLookFor the value or object to look for @returns the index of the object, or -1 if it's not found */ int indexOf (const ElementType& elementToLookFor) const noexcept { const ScopedLockType lock (data.getLock()); int s = 0; int e = data.size(); for (;;) { if (s >= e) return -1; if (elementToLookFor == data.getReference (s)) return s; const int halfway = (s + e) / 2; if (halfway == s) return -1; if (elementToLookFor < data.getReference (halfway)) e = halfway; else s = halfway; } } /** Returns true if the set contains at least one occurrence of an object. @param elementToLookFor the value or object to look for @returns true if the item is found */ bool contains (const ElementType& elementToLookFor) const noexcept { return indexOf (elementToLookFor) >= 0; } //============================================================================== /** Adds a new element to the set, (as long as it's not already in there). Note that if a matching element already exists, the new value will be assigned to the existing one using operator=, so that if there are any differences between the objects which were not recognised by the object's operator==, then the set will always contain a copy of the most recently added one. @param newElement the new object to add to the set @returns true if the value was added, or false if it already existed @see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray */ bool add (const ElementType& newElement) noexcept { const ScopedLockType lock (getLock()); int s = 0; int e = data.size(); while (s < e) { ElementType& elem = data.getReference (s); if (newElement == elem) { elem = newElement; // force an update in case operator== permits differences. return false; } const int halfway = (s + e) / 2; const bool isBeforeHalfway = (newElement < data.getReference (halfway)); if (halfway == s) { if (! isBeforeHalfway) ++s; break; } if (isBeforeHalfway) e = halfway; else s = halfway; } data.insert (s, newElement); return true; } /** Adds elements from an array to this set. @param elementsToAdd the array of elements to add @param numElementsToAdd how many elements are in this other array @see add */ void addArray (const ElementType* elementsToAdd, int numElementsToAdd) noexcept { const ScopedLockType lock (getLock()); while (--numElementsToAdd >= 0) add (*elementsToAdd++); } /** Adds elements from another set to this one. @param setToAddFrom the set from which to copy the elements @param startIndex the first element of the other set to start copying from @param numElementsToAdd how many elements to add from the other set. If this value is negative or greater than the number of available elements, all available elements will be copied. @see add */ template void addSet (const OtherSetType& setToAddFrom, int startIndex = 0, int numElementsToAdd = -1) noexcept { const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock()); { const ScopedLockType lock2 (getLock()); jassert (this != &setToAddFrom); if (this != &setToAddFrom) { if (startIndex < 0) { jassertfalse; startIndex = 0; } if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size()) numElementsToAdd = setToAddFrom.size() - startIndex; if (numElementsToAdd > 0) addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd); } } } //============================================================================== /** Removes an element from the set. This will remove the element at a given index. If the index passed in is out-of-range, nothing will happen. @param indexToRemove the index of the element to remove @returns the element that has been removed @see removeValue, removeRange */ ElementType remove (const int indexToRemove) noexcept { return data.remove (indexToRemove); } /** Removes an item from the set. This will remove the given element from the set, if it's there. @param valueToRemove the object to try to remove @see remove, removeRange */ void removeValue (const ElementType valueToRemove) noexcept { const ScopedLockType lock (getLock()); data.remove (indexOf (valueToRemove)); } /** Removes any elements which are also in another set. @param otherSet the other set in which to look for elements to remove @see removeValuesNotIn, remove, removeValue, removeRange */ template void removeValuesIn (const OtherSetType& otherSet) noexcept { const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock()); const ScopedLockType lock2 (getLock()); if (this == &otherSet) { clear(); } else if (otherSet.size() > 0) { for (int i = data.size(); --i >= 0;) if (otherSet.contains (data.getReference (i))) remove (i); } } /** Removes any elements which are not found in another set. Only elements which occur in this other set will be retained. @param otherSet the set in which to look for elements NOT to remove @see removeValuesIn, remove, removeValue, removeRange */ template void removeValuesNotIn (const OtherSetType& otherSet) noexcept { const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock()); const ScopedLockType lock2 (getLock()); if (this != &otherSet) { if (otherSet.size() <= 0) { clear(); } else { for (int i = data.size(); --i >= 0;) if (! otherSet.contains (data.getReference (i))) remove (i); } } } /** This swaps the contents of this array with those of another array. If you need to exchange two arrays, this is vastly quicker than using copy-by-value because it just swaps their internal pointers. */ template void swapWith (OtherSetType& otherSet) noexcept { data.swapWith (otherSet.data); } //============================================================================== /** Reduces the amount of storage being used by the set. Sets typically allocate slightly more storage than they need, and after removing elements, they may have quite a lot of unused space allocated. This method will reduce the amount of allocated storage to a minimum. */ void minimiseStorageOverheads() noexcept { data.minimiseStorageOverheads(); } /** Increases the set's internal storage to hold a minimum number of elements. Calling this before adding a large known number of elements means that the set won't have to keep dynamically resizing itself as the elements are added, and it'll therefore be more efficient. */ void ensureStorageAllocated (const int minNumElements) { data.ensureStorageAllocated (minNumElements); } //============================================================================== /** Returns the CriticalSection that locks this array. To lock, you can call getLock().enter() and getLock().exit(), or preferably use an object of ScopedLockType as an RAII lock for it. */ inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); } /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; private: //============================================================================== Array data; }; #if JUCE_MSVC #pragma warning (pop) #endif #endif // JUCE_SORTEDSET_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h000066400000000000000000000227351320201440200310100ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SPARSESET_H_INCLUDED #define JUCE_SPARSESET_H_INCLUDED //============================================================================== /** Holds a set of primitive values, storing them as a set of ranges. This container acts like an array, but can efficiently hold large contiguous ranges of values. It's quite a specialised class, mostly useful for things like keeping the set of selected rows in a listbox. The type used as a template parameter must be an integer type, such as int, short, int64, etc. */ template class SparseSet { public: //============================================================================== /** Creates a new empty set. */ SparseSet() { } /** Creates a copy of another SparseSet. */ SparseSet (const SparseSet& other) : values (other.values) { } //============================================================================== /** Clears the set. */ void clear() { values.clear(); } /** Checks whether the set is empty. This is much quicker than using (size() == 0). */ bool isEmpty() const noexcept { return values.size() == 0; } /** Returns the number of values in the set. Because of the way the data is stored, this method can take longer if there are a lot of items in the set. Use isEmpty() for a quick test of whether there are any items. */ Type size() const { Type total (0); for (int i = 0; i < values.size(); i += 2) total += values.getUnchecked (i + 1) - values.getUnchecked (i); return total; } /** Returns one of the values in the set. @param index the index of the value to retrieve, in the range 0 to (size() - 1). @returns the value at this index, or 0 if it's out-of-range */ Type operator[] (Type index) const { for (int i = 0; i < values.size(); i += 2) { const Type start (values.getUnchecked (i)); const Type len (values.getUnchecked (i + 1) - start); if (index < len) return start + index; index -= len; } return Type(); } /** Checks whether a particular value is in the set. */ bool contains (const Type valueToLookFor) const { for (int i = 0; i < values.size(); ++i) if (valueToLookFor < values.getUnchecked(i)) return (i & 1) != 0; return false; } //============================================================================== /** Returns the number of contiguous blocks of values. @see getRange */ int getNumRanges() const noexcept { return values.size() >> 1; } /** Returns one of the contiguous ranges of values stored. @param rangeIndex the index of the range to look up, between 0 and (getNumRanges() - 1) @see getTotalRange */ const Range getRange (const int rangeIndex) const { if (isPositiveAndBelow (rangeIndex, getNumRanges())) return Range (values.getUnchecked (rangeIndex << 1), values.getUnchecked ((rangeIndex << 1) + 1)); return Range(); } /** Returns the range between the lowest and highest values in the set. @see getRange */ Range getTotalRange() const { if (values.size() > 0) { jassert ((values.size() & 1) == 0); return Range (values.getUnchecked (0), values.getUnchecked (values.size() - 1)); } return Range(); } //============================================================================== /** Adds a range of contiguous values to the set. e.g. addRange (Range \ (10, 14)) will add (10, 11, 12, 13) to the set. */ void addRange (const Range range) { jassert (range.getLength() >= 0); if (range.getLength() > 0) { removeRange (range); values.addUsingDefaultSort (range.getStart()); values.addUsingDefaultSort (range.getEnd()); simplify(); } } /** Removes a range of values from the set. e.g. removeRange (Range\ (10, 14)) will remove (10, 11, 12, 13) from the set. */ void removeRange (const Range rangeToRemove) { jassert (rangeToRemove.getLength() >= 0); if (rangeToRemove.getLength() > 0 && values.size() > 0 && rangeToRemove.getStart() < values.getUnchecked (values.size() - 1) && values.getUnchecked(0) < rangeToRemove.getEnd()) { const bool onAtStart = contains (rangeToRemove.getStart() - 1); const Type lastValue (jmin (rangeToRemove.getEnd(), values.getLast())); const bool onAtEnd = contains (lastValue); for (int i = values.size(); --i >= 0;) { if (values.getUnchecked(i) <= lastValue) { while (values.getUnchecked(i) >= rangeToRemove.getStart()) { values.remove (i); if (--i < 0) break; } break; } } if (onAtStart) values.addUsingDefaultSort (rangeToRemove.getStart()); if (onAtEnd) values.addUsingDefaultSort (lastValue); simplify(); } } /** Does an XOR of the values in a given range. */ void invertRange (const Range range) { SparseSet newItems; newItems.addRange (range); for (int i = getNumRanges(); --i >= 0;) newItems.removeRange (getRange (i)); removeRange (range); for (int i = newItems.getNumRanges(); --i >= 0;) addRange (newItems.getRange(i)); } /** Checks whether any part of a given range overlaps any part of this set. */ bool overlapsRange (const Range range) { if (range.getLength() > 0) { for (int i = getNumRanges(); --i >= 0;) { if (values.getUnchecked ((i << 1) + 1) <= range.getStart()) return false; if (values.getUnchecked (i << 1) < range.getEnd()) return true; } } return false; } /** Checks whether the whole of a given range is contained within this one. */ bool containsRange (const Range range) { if (range.getLength() > 0) { for (int i = getNumRanges(); --i >= 0;) { if (values.getUnchecked ((i << 1) + 1) <= range.getStart()) return false; if (values.getUnchecked (i << 1) <= range.getStart() && range.getEnd() <= values.getUnchecked ((i << 1) + 1)) return true; } } return false; } //============================================================================== bool operator== (const SparseSet& other) noexcept { return values == other.values; } bool operator!= (const SparseSet& other) noexcept { return values != other.values; } private: //============================================================================== // alternating start/end values of ranges of values that are present. Array values; void simplify() { jassert ((values.size() & 1) == 0); for (int i = values.size(); --i > 0;) if (values.getUnchecked(i) == values.getUnchecked (i - 1)) values.removeRange (--i, 2); } }; #endif // JUCE_SPARSESET_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp000066400000000000000000001023321320201440200310260ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ enum VariantStreamMarkers { varMarker_Int = 1, varMarker_BoolTrue = 2, varMarker_BoolFalse = 3, varMarker_Double = 4, varMarker_String = 5, varMarker_Int64 = 6, varMarker_Array = 7, varMarker_Binary = 8, varMarker_Undefined = 9 }; //============================================================================== class var::VariantType { public: VariantType() noexcept {} virtual ~VariantType() noexcept {} virtual int toInt (const ValueUnion&) const noexcept { return 0; } virtual int64 toInt64 (const ValueUnion&) const noexcept { return 0; } virtual double toDouble (const ValueUnion&) const noexcept { return 0; } virtual String toString (const ValueUnion&) const { return String::empty; } virtual bool toBool (const ValueUnion&) const noexcept { return false; } virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; } virtual Array* toArray (const ValueUnion&) const noexcept { return nullptr; } virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; } virtual var clone (const var& original) const { return original; } virtual bool isVoid() const noexcept { return false; } virtual bool isUndefined() const noexcept { return false; } virtual bool isInt() const noexcept { return false; } virtual bool isInt64() const noexcept { return false; } virtual bool isBool() const noexcept { return false; } virtual bool isDouble() const noexcept { return false; } virtual bool isString() const noexcept { return false; } virtual bool isObject() const noexcept { return false; } virtual bool isArray() const noexcept { return false; } virtual bool isBinary() const noexcept { return false; } virtual bool isMethod() const noexcept { return false; } virtual void cleanUp (ValueUnion&) const noexcept {} virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; } virtual bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept = 0; virtual void writeToStream (const ValueUnion& data, OutputStream& output) const = 0; }; //============================================================================== class var::VariantType_Void : public var::VariantType { public: VariantType_Void() noexcept {} static const VariantType_Void instance; bool isVoid() const noexcept override { return true; } bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); } }; //============================================================================== class var::VariantType_Undefined : public var::VariantType { public: VariantType_Undefined() noexcept {} static const VariantType_Undefined instance; bool isUndefined() const noexcept override { return true; } String toString (const ValueUnion&) const override { return "undefined"; } bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (1); output.writeByte (varMarker_Undefined); } }; //============================================================================== class var::VariantType_Int : public var::VariantType { public: VariantType_Int() noexcept {} static const VariantType_Int instance; int toInt (const ValueUnion& data) const noexcept override { return data.intValue; }; int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.intValue; }; double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; } String toString (const ValueUnion& data) const override { return String (data.intValue); } bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; } bool isInt() const noexcept override { return true; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { if (otherType.isDouble() || otherType.isInt64() || otherType.isString()) return otherType.equals (otherData, data, *this); return otherType.toInt (otherData) == data.intValue; } void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (5); output.writeByte (varMarker_Int); output.writeInt (data.intValue); } }; //============================================================================== class var::VariantType_Int64 : public var::VariantType { public: VariantType_Int64() noexcept {} static const VariantType_Int64 instance; int toInt (const ValueUnion& data) const noexcept override { return (int) data.int64Value; }; int64 toInt64 (const ValueUnion& data) const noexcept override { return data.int64Value; }; double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; } String toString (const ValueUnion& data) const override { return String (data.int64Value); } bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; } bool isInt64() const noexcept override { return true; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { if (otherType.isDouble() || otherType.isString()) return otherType.equals (otherData, data, *this); return otherType.toInt64 (otherData) == data.int64Value; } void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (9); output.writeByte (varMarker_Int64); output.writeInt64 (data.int64Value); } }; //============================================================================== class var::VariantType_Double : public var::VariantType { public: VariantType_Double() noexcept {} static const VariantType_Double instance; int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; }; int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; }; double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; } String toString (const ValueUnion& data) const override { return String (data.doubleValue, 20); } bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0; } bool isDouble() const noexcept override { return true; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits::epsilon(); } void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (9); output.writeByte (varMarker_Double); output.writeDouble (data.doubleValue); } }; //============================================================================== class var::VariantType_Bool : public var::VariantType { public: VariantType_Bool() noexcept {} static const VariantType_Bool instance; int toInt (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; }; int64 toInt64 (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; }; double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; } String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; } bool isBool() const noexcept override { return true; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.toBool (otherData) == data.boolValue; } void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (1); output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse); } }; //============================================================================== class var::VariantType_String : public var::VariantType { public: VariantType_String() noexcept {} static const VariantType_String instance; void cleanUp (ValueUnion& data) const noexcept override { getString (data)-> ~String(); } void createCopy (ValueUnion& dest, const ValueUnion& source) const override { new (dest.stringValue) String (*getString (source)); } bool isString() const noexcept override { return true; } int toInt (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue(); }; int64 toInt64 (const ValueUnion& data) const noexcept override { return getString (data)->getLargeIntValue(); }; double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); } String toString (const ValueUnion& data) const override { return *getString (data); } bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0 || getString (data)->trim().equalsIgnoreCase ("true") || getString (data)->trim().equalsIgnoreCase ("yes"); } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.toString (otherData) == *getString (data); } void writeToStream (const ValueUnion& data, OutputStream& output) const override { const String* const s = getString (data); const size_t len = s->getNumBytesAsUTF8() + 1; HeapBlock temp (len); s->copyToUTF8 (temp, len); output.writeCompressedInt ((int) (len + 1)); output.writeByte (varMarker_String); output.write (temp, len); } private: static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } }; //============================================================================== class var::VariantType_Object : public var::VariantType { public: VariantType_Object() noexcept {} static const VariantType_Object instance; void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.objectValue = source.objectValue; if (dest.objectValue != nullptr) dest.objectValue->incReferenceCount(); } String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != 0; } ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; } bool isObject() const noexcept override { return true; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.toObject (otherData) == data.objectValue; } var clone (const var& original) const override { if (DynamicObject* d = original.getDynamicObject()) return d->clone().get(); jassertfalse; // can only clone DynamicObjects! return var(); } void writeToStream (const ValueUnion&, OutputStream& output) const override { jassertfalse; // Can't write an object to a stream! output.writeCompressedInt (0); } }; //============================================================================== class var::VariantType_Array : public var::VariantType_Object { public: VariantType_Array() noexcept {} static const VariantType_Array instance; String toString (const ValueUnion&) const override { return "[Array]"; } ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; } bool isArray() const noexcept override { return true; } Array* toArray (const ValueUnion& data) const noexcept override { if (RefCountedArray* a = dynamic_cast (data.objectValue)) return &(a->array); return nullptr; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { const Array* const thisArray = toArray (data); const Array* const otherArray = otherType.toArray (otherData); return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray); } var clone (const var& original) const override { Array arrayCopy; if (const Array* array = toArray (original.value)) for (int i = 0; i < array->size(); ++i) arrayCopy.add (array->getReference(i).clone()); return var (arrayCopy); } void writeToStream (const ValueUnion& data, OutputStream& output) const override { if (const Array* array = toArray (data)) { MemoryOutputStream buffer (512); const int numItems = array->size(); buffer.writeCompressedInt (numItems); for (int i = 0; i < numItems; ++i) array->getReference(i).writeToStream (buffer); output.writeCompressedInt (1 + (int) buffer.getDataSize()); output.writeByte (varMarker_Array); output << buffer; } } struct RefCountedArray : public ReferenceCountedObject { RefCountedArray (const Array& a) : array (a) { incReferenceCount(); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS RefCountedArray (Array&& a) : array (static_cast&&> (a)) { incReferenceCount(); } #endif Array array; }; }; //============================================================================== class var::VariantType_Binary : public var::VariantType { public: VariantType_Binary() noexcept {} static const VariantType_Binary instance; void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; } void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); } String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); } bool isBinary() const noexcept override { return true; } MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { const MemoryBlock* const otherBlock = otherType.toBinary (otherData); return otherBlock != nullptr && *otherBlock == *data.binaryValue; } void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (1 + (int) data.binaryValue->getSize()); output.writeByte (varMarker_Binary); output << *data.binaryValue; } }; //============================================================================== class var::VariantType_Method : public var::VariantType { public: VariantType_Method() noexcept {} static const VariantType_Method instance; String toString (const ValueUnion&) const override { return "Method"; } bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; } bool isMethod() const noexcept override { return true; } bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.isMethod() && otherData.methodValue == data.methodValue; } void writeToStream (const ValueUnion&, OutputStream& output) const override { jassertfalse; // Can't write a method to a stream! output.writeCompressedInt (0); } }; //============================================================================== const var::VariantType_Void var::VariantType_Void::instance; const var::VariantType_Undefined var::VariantType_Undefined::instance; const var::VariantType_Int var::VariantType_Int::instance; const var::VariantType_Int64 var::VariantType_Int64::instance; const var::VariantType_Bool var::VariantType_Bool::instance; const var::VariantType_Double var::VariantType_Double::instance; const var::VariantType_String var::VariantType_String::instance; const var::VariantType_Object var::VariantType_Object::instance; const var::VariantType_Array var::VariantType_Array::instance; const var::VariantType_Binary var::VariantType_Binary::instance; const var::VariantType_Method var::VariantType_Method::instance; //============================================================================== var::var() noexcept : type (&VariantType_Void::instance) {} var::var (const VariantType& t) noexcept : type (&t) {} var::~var() noexcept { type->cleanUp (value); } const var var::null; //============================================================================== var::var (const var& valueToCopy) : type (valueToCopy.type) { type->createCopy (value, valueToCopy.value); } var::var (const int v) noexcept : type (&VariantType_Int::instance) { value.intValue = v; } var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; } var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; } var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; } var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = m; } var::var (const Array& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); } var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } var::var (const void* v, size_t sz) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); } var::var (const MemoryBlock& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); } var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance) { value.objectValue = object; if (object != nullptr) object->incReferenceCount(); } var var::undefined() noexcept { return var (VariantType_Undefined::instance); } //============================================================================== bool var::isVoid() const noexcept { return type->isVoid(); } bool var::isUndefined() const noexcept { return type->isUndefined(); } bool var::isInt() const noexcept { return type->isInt(); } bool var::isInt64() const noexcept { return type->isInt64(); } bool var::isBool() const noexcept { return type->isBool(); } bool var::isDouble() const noexcept { return type->isDouble(); } bool var::isString() const noexcept { return type->isString(); } bool var::isObject() const noexcept { return type->isObject(); } bool var::isArray() const noexcept { return type->isArray(); } bool var::isBinaryData() const noexcept { return type->isBinary(); } bool var::isMethod() const noexcept { return type->isMethod(); } var::operator int() const noexcept { return type->toInt (value); } var::operator int64() const noexcept { return type->toInt64 (value); } var::operator bool() const noexcept { return type->toBool (value); } var::operator float() const noexcept { return (float) type->toDouble (value); } var::operator double() const noexcept { return type->toDouble (value); } String var::toString() const { return type->toString (value); } var::operator String() const { return type->toString (value); } ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); } Array* var::getArray() const noexcept { return type->toArray (value); } MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); } DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast (getObject()); } //============================================================================== void var::swapWith (var& other) noexcept { std::swap (type, other.type); std::swap (value, other.value); } var& var::operator= (const var& v) { type->cleanUp (value); type = v.type; type->createCopy (value, v.value); return *this; } var& var::operator= (const int v) { type->cleanUp (value); type = &VariantType_Int::instance; value.intValue = v; return *this; } var& var::operator= (const int64 v) { type->cleanUp (value); type = &VariantType_Int64::instance; value.int64Value = v; return *this; } var& var::operator= (const bool v) { type->cleanUp (value); type = &VariantType_Bool::instance; value.boolValue = v; return *this; } var& var::operator= (const double v) { type->cleanUp (value); type = &VariantType_Double::instance; value.doubleValue = v; return *this; } var& var::operator= (const char* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } var& var::operator= (const Array& v) { var v2 (v); swapWith (v2); return *this; } var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; } var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS var::var (var&& other) noexcept : type (other.type), value (other.value) { other.type = &VariantType_Void::instance; } var& var::operator= (var&& other) noexcept { swapWith (other); return *this; } var::var (String&& v) : type (&VariantType_String::instance) { new (value.stringValue) String (static_cast (v)); } var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (static_cast (v)); } var::var (Array&& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray (static_cast&&> (v)); } var& var::operator= (String&& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (static_cast (v)); return *this; } #endif //============================================================================== bool var::equals (const var& other) const noexcept { return type->equals (value, other.value, *other.type); } bool var::equalsWithSameType (const var& other) const noexcept { return type == other.type && equals (other); } bool var::hasSameTypeAs (const var& other) const noexcept { return type == other.type; } bool operator== (const var& v1, const var& v2) noexcept { return v1.equals (v2); } bool operator!= (const var& v1, const var& v2) noexcept { return ! v1.equals (v2); } bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; } bool operator!= (const var& v1, const String& v2) { return v1.toString() != v2; } bool operator== (const var& v1, const char* const v2) { return v1.toString() == v2; } bool operator!= (const var& v1, const char* const v2) { return v1.toString() != v2; } //============================================================================== var var::clone() const noexcept { return type->clone (*this); } //============================================================================== const var& var::operator[] (const Identifier& propertyName) const { if (DynamicObject* const o = getDynamicObject()) return o->getProperty (propertyName); return var::null; } const var& var::operator[] (const char* const propertyName) const { return operator[] (Identifier (propertyName)); } var var::getProperty (const Identifier& propertyName, const var& defaultReturnValue) const { if (DynamicObject* const o = getDynamicObject()) return o->getProperties().getWithDefault (propertyName, defaultReturnValue); return defaultReturnValue; } var::NativeFunction var::getNativeFunction() const { return isMethod() ? value.methodValue : nullptr; } var var::invoke (const Identifier& method, const var* arguments, int numArguments) const { if (DynamicObject* const o = getDynamicObject()) return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments)); return var(); } var var::call (const Identifier& method) const { return invoke (method, nullptr, 0); } var var::call (const Identifier& method, const var& arg1) const { return invoke (method, &arg1, 1); } var var::call (const Identifier& method, const var& arg1, const var& arg2) const { var args[] = { arg1, arg2 }; return invoke (method, args, 2); } var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3) { var args[] = { arg1, arg2, arg3 }; return invoke (method, args, 3); } var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const { var args[] = { arg1, arg2, arg3, arg4 }; return invoke (method, args, 4); } var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const { var args[] = { arg1, arg2, arg3, arg4, arg5 }; return invoke (method, args, 5); } //============================================================================== int var::size() const { if (const Array* const array = getArray()) return array->size(); return 0; } const var& var::operator[] (int arrayIndex) const { const Array* const array = getArray(); // When using this method, the var must actually be an array, and the index // must be in-range! jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size())); return array->getReference (arrayIndex); } var& var::operator[] (int arrayIndex) { const Array* const array = getArray(); // When using this method, the var must actually be an array, and the index // must be in-range! jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size())); return array->getReference (arrayIndex); } Array* var::convertToArray() { if (Array* array = getArray()) return array; Array tempVar; if (! isVoid()) tempVar.add (*this); *this = tempVar; return getArray(); } void var::append (const var& n) { convertToArray()->add (n); } void var::remove (const int index) { if (Array* const array = getArray()) array->remove (index); } void var::insert (const int index, const var& n) { convertToArray()->insert (index, n); } void var::resize (const int numArrayElementsWanted) { convertToArray()->resize (numArrayElementsWanted); } int var::indexOf (const var& n) const { if (const Array* const array = getArray()) return array->indexOf (n); return -1; } //============================================================================== void var::writeToStream (OutputStream& output) const { type->writeToStream (value, output); } var var::readFromStream (InputStream& input) { const int numBytes = input.readCompressedInt(); if (numBytes > 0) { switch (input.readByte()) { case varMarker_Int: return var (input.readInt()); case varMarker_Int64: return var (input.readInt64()); case varMarker_BoolTrue: return var (true); case varMarker_BoolFalse: return var (false); case varMarker_Double: return var (input.readDouble()); case varMarker_String: { MemoryOutputStream mo; mo.writeFromInputStream (input, numBytes - 1); return var (mo.toUTF8()); } case varMarker_Binary: { MemoryBlock mb ((size_t) numBytes - 1); if (numBytes > 1) { const int numRead = input.read (mb.getData(), numBytes - 1); mb.setSize ((size_t) numRead); } return var (mb); } case varMarker_Array: { var v; Array* const destArray = v.convertToArray(); for (int i = input.readCompressedInt(); --i >= 0;) destArray->add (readFromStream (input)); return v; } default: input.skipNextBytes (numBytes - 1); break; } } return var(); } var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept : thisObject (t), arguments (args), numArguments (numArgs) {} libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h000066400000000000000000000345301320201440200304770ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_VARIANT_H_INCLUDED #define JUCE_VARIANT_H_INCLUDED //============================================================================== /** A variant class, that can be used to hold a range of primitive values. A var object can hold a range of simple primitive values, strings, or any kind of ReferenceCountedObject. The var class is intended to act like the kind of values used in dynamic scripting languages. You can save/load var objects either in a small, proprietary binary format using writeToStream()/readFromStream(), or as JSON by using the JSON class. @see JSON, DynamicObject */ class JUCE_API var { public: //============================================================================== /** This structure is passed to a NativeFunction callback, and contains invocation details about the function's arguments and context. */ struct NativeFunctionArgs { NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept; const var& thisObject; const var* arguments; int numArguments; JUCE_DECLARE_NON_COPYABLE (NativeFunctionArgs) }; typedef var (*NativeFunction) (const NativeFunctionArgs&); //============================================================================== /** Creates a void variant. */ var() noexcept; /** Destructor. */ ~var() noexcept; /** A static var object that can be used where you need an empty variant object. */ static const var null; var (const var& valueToCopy); var (int value) noexcept; var (int64 value) noexcept; var (bool value) noexcept; var (double value) noexcept; var (const char* value); var (const wchar_t* value); var (const String& value); var (const Array& value); var (ReferenceCountedObject* object); var (NativeFunction method) noexcept; var (const void* binaryData, size_t dataSize); var (const MemoryBlock& binaryData); var& operator= (const var& valueToCopy); var& operator= (int value); var& operator= (int64 value); var& operator= (bool value); var& operator= (double value); var& operator= (const char* value); var& operator= (const wchar_t* value); var& operator= (const String& value); var& operator= (const Array& value); var& operator= (ReferenceCountedObject* object); var& operator= (NativeFunction method); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS var (var&& other) noexcept; var (String&& value); var (MemoryBlock&& binaryData); var (Array&& value); var& operator= (var&& other) noexcept; var& operator= (String&& value); #endif void swapWith (var& other) noexcept; /** Returns a var object that can be used where you need the javascript "undefined" value. */ static var undefined() noexcept; //============================================================================== operator int() const noexcept; operator int64() const noexcept; operator bool() const noexcept; operator float() const noexcept; operator double() const noexcept; operator String() const; String toString() const; /** If this variant holds an array, this provides access to it. NOTE: Beware when you use this - the array pointer is only valid for the lifetime of the variant that returned it, so be very careful not to call this method on temporary var objects that are the return-value of a function, and which may go out of scope before you use the array! */ Array* getArray() const noexcept; /** If this variant holds a memory block, this provides access to it. NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime of the variant that returned it, so be very careful not to call this method on temporary var objects that are the return-value of a function, and which may go out of scope before you use the MemoryBlock! */ MemoryBlock* getBinaryData() const noexcept; ReferenceCountedObject* getObject() const noexcept; DynamicObject* getDynamicObject() const noexcept; //============================================================================== bool isVoid() const noexcept; bool isUndefined() const noexcept; bool isInt() const noexcept; bool isInt64() const noexcept; bool isBool() const noexcept; bool isDouble() const noexcept; bool isString() const noexcept; bool isObject() const noexcept; bool isArray() const noexcept; bool isBinaryData() const noexcept; bool isMethod() const noexcept; /** Returns true if this var has the same value as the one supplied. Note that this ignores the type, so a string var "123" and an integer var with the value 123 are considered to be equal. @see equalsWithSameType */ bool equals (const var& other) const noexcept; /** Returns true if this var has the same value and type as the one supplied. This differs from equals() because e.g. "123" and 123 will be considered different. @see equals */ bool equalsWithSameType (const var& other) const noexcept; /** Returns true if this var has the same type as the one supplied. */ bool hasSameTypeAs (const var& other) const noexcept; /** Returns a deep copy of this object. For simple types this just returns a copy, but if the object contains any arrays or DynamicObjects, they will be cloned (recursively). */ var clone() const noexcept; //============================================================================== /** If the var is an array, this returns the number of elements. If the var isn't actually an array, this will return 0. */ int size() const; /** If the var is an array, this can be used to return one of its elements. To call this method, you must make sure that the var is actually an array, and that the index is a valid number. If these conditions aren't met, behaviour is undefined. For more control over the array's contents, you can call getArray() and manipulate it directly as an Array\. */ const var& operator[] (int arrayIndex) const; /** If the var is an array, this can be used to return one of its elements. To call this method, you must make sure that the var is actually an array, and that the index is a valid number. If these conditions aren't met, behaviour is undefined. For more control over the array's contents, you can call getArray() and manipulate it directly as an Array\. */ var& operator[] (int arrayIndex); /** Appends an element to the var, converting it to an array if it isn't already one. If the var isn't an array, it will be converted to one, and if its value was non-void, this value will be kept as the first element of the new array. The parameter value will then be appended to it. For more control over the array's contents, you can call getArray() and manipulate it directly as an Array\. */ void append (const var& valueToAppend); /** Inserts an element to the var, converting it to an array if it isn't already one. If the var isn't an array, it will be converted to one, and if its value was non-void, this value will be kept as the first element of the new array. The parameter value will then be inserted into it. For more control over the array's contents, you can call getArray() and manipulate it directly as an Array\. */ void insert (int index, const var& value); /** If the var is an array, this removes one of its elements. If the index is out-of-range or the var isn't an array, nothing will be done. For more control over the array's contents, you can call getArray() and manipulate it directly as an Array\. */ void remove (int index); /** Treating the var as an array, this resizes it to contain the specified number of elements. If the var isn't an array, it will be converted to one, and if its value was non-void, this value will be kept as the first element of the new array before resizing. For more control over the array's contents, you can call getArray() and manipulate it directly as an Array\. */ void resize (int numArrayElementsWanted); /** If the var is an array, this searches it for the first occurrence of the specified value, and returns its index. If the var isn't an array, or if the value isn't found, this returns -1. */ int indexOf (const var& value) const; //============================================================================== /** If this variant is an object, this returns one of its properties. */ const var& operator[] (const Identifier& propertyName) const; /** If this variant is an object, this returns one of its properties. */ const var& operator[] (const char* propertyName) const; /** If this variant is an object, this returns one of its properties, or a default fallback value if the property is not set. */ var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const; /** Invokes a named method call with no arguments. */ var call (const Identifier& method) const; /** Invokes a named method call with one argument. */ var call (const Identifier& method, const var& arg1) const; /** Invokes a named method call with 2 arguments. */ var call (const Identifier& method, const var& arg1, const var& arg2) const; /** Invokes a named method call with 3 arguments. */ var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3); /** Invokes a named method call with 4 arguments. */ var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; /** Invokes a named method call with 5 arguments. */ var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const; /** Invokes a named method call with a list of arguments. */ var invoke (const Identifier& method, const var* arguments, int numArguments) const; /** If this object is a method, this returns the function pointer. */ NativeFunction getNativeFunction() const; //============================================================================== /** Writes a binary representation of this value to a stream. The data can be read back later using readFromStream(). @see JSON */ void writeToStream (OutputStream& output) const; /** Reads back a stored binary representation of a value. The data in the stream must have been written using writeToStream(), or this will have unpredictable results. @see JSON */ static var readFromStream (InputStream& input); private: //============================================================================== class VariantType; friend class VariantType; class VariantType_Void; friend class VariantType_Void; class VariantType_Undefined; friend class VariantType_Undefined; class VariantType_Int; friend class VariantType_Int; class VariantType_Int64; friend class VariantType_Int64; class VariantType_Double; friend class VariantType_Double; class VariantType_Bool; friend class VariantType_Bool; class VariantType_String; friend class VariantType_String; class VariantType_Object; friend class VariantType_Object; class VariantType_Array; friend class VariantType_Array; class VariantType_Binary; friend class VariantType_Binary; class VariantType_Method; friend class VariantType_Method; union ValueUnion { int intValue; int64 int64Value; bool boolValue; double doubleValue; char stringValue [sizeof (String)]; ReferenceCountedObject* objectValue; MemoryBlock* binaryValue; NativeFunction methodValue; }; const VariantType* type; ValueUnion value; Array* convertToArray(); var (const VariantType&) noexcept; }; /** Compares the values of two var objects, using the var::equals() comparison. */ bool operator== (const var& v1, const var& v2) noexcept; /** Compares the values of two var objects, using the var::equals() comparison. */ bool operator!= (const var& v1, const var& v2) noexcept; bool operator== (const var& v1, const String& v2); bool operator!= (const var& v1, const String& v2); bool operator== (const var& v1, const char* v2); bool operator!= (const var& v1, const char* v2); #endif // JUCE_VARIANT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/000077500000000000000000000000001320201440200246445ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp000066400000000000000000000137421320201440200320430ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ DirectoryIterator::DirectoryIterator (const File& directory, bool recursive, const String& pattern, const int type) : wildCards (parseWildcards (pattern)), fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern), wildCard (pattern), path (File::addTrailingSeparator (directory.getFullPathName())), index (-1), totalNumFiles (-1), whatToLookFor (type), isRecursive (recursive), hasBeenAdvanced (false) { // you have to specify the type of files you're looking for! jassert ((type & (File::findFiles | File::findDirectories)) != 0); jassert (type > 0 && type <= 7); } DirectoryIterator::~DirectoryIterator() { } StringArray DirectoryIterator::parseWildcards (const String& pattern) { StringArray s; s.addTokens (pattern, ";,", "\"'"); s.trim(); s.removeEmptyStrings(); return s; } bool DirectoryIterator::fileMatches (const StringArray& wildCards, const String& filename) { for (int i = 0; i < wildCards.size(); ++i) if (filename.matchesWildcard (wildCards[i], ! File::areFileNamesCaseSensitive())) return true; return false; } bool DirectoryIterator::next() { return next (nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResult, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { for (;;) { hasBeenAdvanced = true; if (subIterator != nullptr) { if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly)) return true; subIterator = nullptr; } String filename; bool isDirectory, isHidden = false, shouldContinue = false; while (fileFinder.next (filename, &isDirectory, (isHiddenResult != nullptr || (whatToLookFor & File::ignoreHiddenFiles) != 0) ? &isHidden : nullptr, fileSize, modTime, creationTime, isReadOnly)) { ++index; if (! filename.containsOnly (".")) { bool matches = false; if (isDirectory) { if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden)) subIterator = new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename), true, wildCard, whatToLookFor); matches = (whatToLookFor & File::findDirectories) != 0; } else { matches = (whatToLookFor & File::findFiles) != 0; } // if we're not relying on the OS iterator to do the wildcard match, do it now.. if (matches && (isRecursive || wildCards.size() > 1)) matches = fileMatches (wildCards, filename); if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0) matches = ! isHidden; if (matches) { currentFile = File::createFileWithoutCheckingPath (path + filename); if (isHiddenResult != nullptr) *isHiddenResult = isHidden; if (isDirResult != nullptr) *isDirResult = isDirectory; return true; } if (subIterator != nullptr) { shouldContinue = true; break; } } } if (! shouldContinue) return false; } } const File& DirectoryIterator::getFile() const { if (subIterator != nullptr && subIterator->hasBeenAdvanced) return subIterator->getFile(); // You need to call DirectoryIterator::next() before asking it for the file that it found! jassert (hasBeenAdvanced); return currentFile; } float DirectoryIterator::getEstimatedProgress() const { if (totalNumFiles < 0) totalNumFiles = File (path).getNumberOfChildFiles (File::findFilesAndDirectories); if (totalNumFiles <= 0) return 0.0f; const float detailedIndex = (subIterator != nullptr) ? index + subIterator->getEstimatedProgress() : (float) index; return detailedIndex / totalNumFiles; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h000066400000000000000000000142701320201440200315050ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_DIRECTORYITERATOR_H_INCLUDED #define JUCE_DIRECTORYITERATOR_H_INCLUDED //============================================================================== /** Searches through the files in a directory, returning each file that is found. A DirectoryIterator will search through a directory and its subdirectories using a wildcard filepattern match. If you may be scanning a large number of files, it's usually smarter to use this class than File::findChildFiles() because it allows you to stop at any time, rather than having to wait for the entire scan to finish before getting the results. It also provides an estimate of its progress, using a (highly inaccurate!) algorithm. */ class JUCE_API DirectoryIterator { public: //============================================================================== /** Creates a DirectoryIterator for a given directory. After creating one of these, call its next() method to get the first file - e.g. @code DirectoryIterator iter (File ("/animals/mooses"), true, "*.moose"); while (iter.next()) { File theFileItFound (iter.getFile()); ... etc } @endcode @param directory the directory to search in @param isRecursive whether all the subdirectories should also be searched @param wildCard the file pattern to match. This may contain multiple patterns separated by a semi-colon or comma, e.g. "*.jpg;*.png" @param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying whether to look for files, directories, or both. */ DirectoryIterator (const File& directory, bool isRecursive, const String& wildCard = "*", int whatToLookFor = File::findFiles); /** Destructor. */ ~DirectoryIterator(); /** Moves the iterator along to the next file. @returns true if a file was found (you can then use getFile() to see what it was) - or false if there are no more matching files. */ bool next(); /** Moves the iterator along to the next file, and returns various properties of that file. If you need to find out details about the file, it's more efficient to call this method than to call the normal next() method and then find out the details afterwards. All the parameters are optional, so pass null pointers for any items that you're not interested in. @returns true if a file was found (you can then use getFile() to see what it was) - or false if there are no more matching files. If it returns false, then none of the parameters will be filled-in. */ bool next (bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly); /** Returns the file that the iterator is currently pointing at. The result of this call is only valid after a call to next() has returned true. */ const File& getFile() const; /** Returns a guess of how far through the search the iterator has got. @returns a value 0.0 to 1.0 to show the progress, although this won't be very accurate. */ float getEstimatedProgress() const; private: //============================================================================== class NativeIterator { public: NativeIterator (const File& directory, const String& wildCard); ~NativeIterator(); bool next (String& filenameFound, bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly); class Pimpl; private: friend class DirectoryIterator; friend struct ContainerDeletePolicy; ScopedPointer pimpl; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator) }; friend struct ContainerDeletePolicy; StringArray wildCards; NativeIterator fileFinder; String wildCard, path; int index; mutable int totalNumFiles; const int whatToLookFor; const bool isRecursive; bool hasBeenAdvanced; ScopedPointer subIterator; File currentFile; static StringArray parseWildcards (const String& pattern); static bool fileMatches (const StringArray& wildCards, const String& filename); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator) }; #endif // JUCE_DIRECTORYITERATOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_File.cpp000066400000000000000000001077261320201440200272520ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ File::File (const String& fullPathName) : fullPath (parseAbsolutePath (fullPathName)) { } File File::createFileWithoutCheckingPath (const String& path) noexcept { File f; f.fullPath = path; return f; } File::File (const File& other) : fullPath (other.fullPath) { } File& File::operator= (const String& newPath) { fullPath = parseAbsolutePath (newPath); return *this; } File& File::operator= (const File& other) { fullPath = other.fullPath; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS File::File (File&& other) noexcept : fullPath (static_cast (other.fullPath)) { } File& File::operator= (File&& other) noexcept { fullPath = static_cast (other.fullPath); return *this; } #endif const File File::nonexistent; //============================================================================== String File::parseAbsolutePath (const String& p) { if (p.isEmpty()) return String(); #if JUCE_WINDOWS // Windows.. String path (p.replaceCharacter ('/', '\\')); if (path.startsWithChar (separator)) { if (path[1] != separator) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; } } else if (! path.containsChar (':')) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #else // Mac or Linux.. // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here // to catch anyone who's trying to run code that was written on Windows with hard-coded path names. // If that's why you've ended up here, use File::getChildFile() to build your paths instead. jassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\'))); String path (p); if (path.startsWithChar ('~')) { if (path[1] == separator || path[1] == 0) { // expand a name of the form "~/abc" path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() + path.substring (1); } else { // expand a name of type "~dave/abc" const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); if (struct passwd* const pw = getpwnam (userName.toUTF8())) path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false); } } else if (! path.startsWithChar (separator)) { #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS if (! (path.startsWith ("./") || path.startsWith ("../"))) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; #if JUCE_LOG_ASSERTIONS Logger::writeToLog ("Illegal absolute path: " + path); #endif } #endif return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #endif while (path.endsWithChar (separator) && path != separatorString) // careful not to turn a single "/" into an empty string. path = path.dropLastCharacters (1); return path; } String File::addTrailingSeparator (const String& path) { return path.endsWithChar (separator) ? path : path + separator; } //============================================================================== #if JUCE_LINUX #define NAMES_ARE_CASE_SENSITIVE 1 #endif bool File::areFileNamesCaseSensitive() { #if NAMES_ARE_CASE_SENSITIVE return true; #else return false; #endif } static int compareFilenames (const String& name1, const String& name2) noexcept { #if NAMES_ARE_CASE_SENSITIVE return name1.compare (name2); #else return name1.compareIgnoreCase (name2); #endif } bool File::operator== (const File& other) const { return compareFilenames (fullPath, other.fullPath) == 0; } bool File::operator!= (const File& other) const { return compareFilenames (fullPath, other.fullPath) != 0; } bool File::operator< (const File& other) const { return compareFilenames (fullPath, other.fullPath) < 0; } bool File::operator> (const File& other) const { return compareFilenames (fullPath, other.fullPath) > 0; } //============================================================================== bool File::setReadOnly (const bool shouldBeReadOnly, const bool applyRecursively) const { bool worked = true; if (applyRecursively && isDirectory()) { Array subFiles; findChildFiles (subFiles, File::findFilesAndDirectories, false); for (int i = subFiles.size(); --i >= 0;) worked = subFiles.getReference(i).setReadOnly (shouldBeReadOnly, true) && worked; } return setFileReadOnlyInternal (shouldBeReadOnly) && worked; } bool File::setExecutePermission (bool shouldBeExecutable) const { return setFileExecutableInternal (shouldBeExecutable); } bool File::deleteRecursively() const { bool worked = true; if (isDirectory()) { Array subFiles; findChildFiles (subFiles, File::findFilesAndDirectories, false); for (int i = subFiles.size(); --i >= 0;) worked = subFiles.getReference(i).deleteRecursively() && worked; } return deleteFile() && worked; } bool File::moveFileTo (const File& newFile) const { if (newFile.fullPath == fullPath) return true; if (! exists()) return false; #if ! NAMES_ARE_CASE_SENSITIVE if (*this != newFile) #endif if (! newFile.deleteFile()) return false; return moveInternal (newFile); } bool File::copyFileTo (const File& newFile) const { return (*this == newFile) || (exists() && newFile.deleteFile() && copyInternal (newFile)); } bool File::copyDirectoryTo (const File& newDirectory) const { if (isDirectory() && newDirectory.createDirectory()) { Array subFiles; findChildFiles (subFiles, File::findFiles, false); for (int i = 0; i < subFiles.size(); ++i) if (! subFiles.getReference(i).copyFileTo (newDirectory.getChildFile (subFiles.getReference(i).getFileName()))) return false; subFiles.clear(); findChildFiles (subFiles, File::findDirectories, false); for (int i = 0; i < subFiles.size(); ++i) if (! subFiles.getReference(i).copyDirectoryTo (newDirectory.getChildFile (subFiles.getReference(i).getFileName()))) return false; return true; } return false; } //============================================================================== String File::getPathUpToLastSlash() const { const int lastSlash = fullPath.lastIndexOfChar (separator); if (lastSlash > 0) return fullPath.substring (0, lastSlash); if (lastSlash == 0) return separatorString; return fullPath; } File File::getParentDirectory() const { File f; f.fullPath = getPathUpToLastSlash(); return f; } //============================================================================== String File::getFileName() const { return fullPath.substring (fullPath.lastIndexOfChar (separator) + 1); } String File::getFileNameWithoutExtension() const { const int lastSlash = fullPath.lastIndexOfChar (separator) + 1; const int lastDot = fullPath.lastIndexOfChar ('.'); if (lastDot > lastSlash) return fullPath.substring (lastSlash, lastDot); return fullPath.substring (lastSlash); } bool File::isAChildOf (const File& potentialParent) const { if (potentialParent.fullPath.isEmpty()) return false; const String ourPath (getPathUpToLastSlash()); if (compareFilenames (potentialParent.fullPath, ourPath) == 0) return true; if (potentialParent.fullPath.length() >= ourPath.length()) return false; return getParentDirectory().isAChildOf (potentialParent); } int File::hashCode() const { return fullPath.hashCode(); } int64 File::hashCode64() const { return fullPath.hashCode64(); } //============================================================================== bool File::isAbsolutePath (StringRef path) { return path.text[0] == separator #if JUCE_WINDOWS || (path.isNotEmpty() && path.text[1] == ':'); #else || path.text[0] == '~'; #endif } File File::getChildFile (StringRef relativePath) const { if (isAbsolutePath (relativePath)) return File (String (relativePath.text)); if (relativePath[0] != '.') return File (addTrailingSeparator (fullPath) + relativePath); String path (fullPath); // It's relative, so remove any ../ or ./ bits at the start.. #if JUCE_WINDOWS if (relativePath.text.indexOf ((juce_wchar) '/') >= 0) return getChildFile (String (relativePath.text).replaceCharacter ('/', '\\')); #endif while (relativePath[0] == '.') { const juce_wchar secondChar = relativePath[1]; if (secondChar == '.') { const juce_wchar thirdChar = relativePath[2]; if (thirdChar == 0 || thirdChar == separator) { const int lastSlash = path.lastIndexOfChar (separator); if (lastSlash >= 0) path = path.substring (0, lastSlash); relativePath = relativePath.text + (thirdChar == 0 ? 2 : 3); } else { break; } } else if (secondChar == separator) { relativePath = relativePath.text + 2; } else { break; } } return File (addTrailingSeparator (path) + relativePath); } File File::getSiblingFile (StringRef fileName) const { return getParentDirectory().getChildFile (fileName); } //============================================================================== String File::descriptionOfSizeInBytes (const int64 bytes) { const char* suffix; double divisor = 0; if (bytes == 1) { suffix = " byte"; } else if (bytes < 1024) { suffix = " bytes"; } else if (bytes < 1024 * 1024) { suffix = " KB"; divisor = 1024.0; } else if (bytes < 1024 * 1024 * 1024) { suffix = " MB"; divisor = 1024.0 * 1024.0; } else { suffix = " GB"; divisor = 1024.0 * 1024.0 * 1024.0; } return (divisor > 0 ? String (bytes / divisor, 1) : String (bytes)) + suffix; } //============================================================================== Result File::create() const { if (exists()) return Result::ok(); const File parentDir (getParentDirectory()); if (parentDir == *this) return Result::fail ("Cannot create parent directory"); Result r (parentDir.createDirectory()); if (r.wasOk()) { FileOutputStream fo (*this, 8); r = fo.getStatus(); } return r; } Result File::createDirectory() const { if (isDirectory()) return Result::ok(); const File parentDir (getParentDirectory()); if (parentDir == *this) return Result::fail ("Cannot create parent directory"); Result r (parentDir.createDirectory()); if (r.wasOk()) r = createDirectoryInternal (fullPath.trimCharactersAtEnd (separatorString)); return r; } //============================================================================== Time File::getLastModificationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (m); } Time File::getLastAccessTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (a); } Time File::getCreationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (c); } bool File::setLastModificationTime (Time t) const { return setFileTimesInternal (t.toMilliseconds(), 0, 0); } bool File::setLastAccessTime (Time t) const { return setFileTimesInternal (0, t.toMilliseconds(), 0); } bool File::setCreationTime (Time t) const { return setFileTimesInternal (0, 0, t.toMilliseconds()); } //============================================================================== bool File::loadFileAsData (MemoryBlock& destBlock) const { if (! existsAsFile()) return false; FileInputStream in (*this); return in.openedOk() && getSize() == (int64) in.readIntoMemoryBlock (destBlock); } String File::loadFileAsString() const { if (! existsAsFile()) return String(); FileInputStream in (*this); return in.openedOk() ? in.readEntireStreamAsString() : String(); } void File::readLines (StringArray& destLines) const { destLines.addLines (loadFileAsString()); } //============================================================================== int File::findChildFiles (Array& results, const int whatToLookFor, const bool searchRecursively, const String& wildCardPattern) const { int total = 0; for (DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor); di.next();) { results.add (di.getFile()); ++total; } return total; } int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const { int total = 0; for (DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor); di.next();) ++total; return total; } bool File::containsSubDirectories() const { if (! isDirectory()) return false; DirectoryIterator di (*this, false, "*", findDirectories); return di.next(); } //============================================================================== File File::getNonexistentChildFile (const String& suggestedPrefix, const String& suffix, bool putNumbersInBrackets) const { File f (getChildFile (suggestedPrefix + suffix)); if (f.exists()) { int number = 1; String prefix (suggestedPrefix); // remove any bracketed numbers that may already be on the end.. if (prefix.trim().endsWithChar (')')) { putNumbersInBrackets = true; const int openBracks = prefix.lastIndexOfChar ('('); const int closeBracks = prefix.lastIndexOfChar (')'); if (openBracks > 0 && closeBracks > openBracks && prefix.substring (openBracks + 1, closeBracks).containsOnly ("0123456789")) { number = prefix.substring (openBracks + 1, closeBracks).getIntValue(); prefix = prefix.substring (0, openBracks); } } // also use brackets if it ends in a digit. putNumbersInBrackets = putNumbersInBrackets || CharacterFunctions::isDigit (prefix.getLastCharacter()); do { String newName (prefix); if (putNumbersInBrackets) newName << '(' << ++number << ')'; else newName << ++number; f = getChildFile (newName + suffix); } while (f.exists()); } return f; } File File::getNonexistentSibling (const bool putNumbersInBrackets) const { if (! exists()) return *this; return getParentDirectory().getNonexistentChildFile (getFileNameWithoutExtension(), getFileExtension(), putNumbersInBrackets); } //============================================================================== String File::getFileExtension() const { const int indexOfDot = fullPath.lastIndexOfChar ('.'); if (indexOfDot > fullPath.lastIndexOfChar (separator)) return fullPath.substring (indexOfDot); return String(); } bool File::hasFileExtension (StringRef possibleSuffix) const { if (possibleSuffix.isEmpty()) return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (separator); const int semicolon = possibleSuffix.text.indexOf ((juce_wchar) ';'); if (semicolon >= 0) return hasFileExtension (String (possibleSuffix.text).substring (0, semicolon).trimEnd()) || hasFileExtension ((possibleSuffix.text + (semicolon + 1)).findEndOfWhitespace()); if (fullPath.endsWithIgnoreCase (possibleSuffix)) { if (possibleSuffix.text[0] == '.') return true; const int dotPos = fullPath.length() - possibleSuffix.length() - 1; if (dotPos >= 0) return fullPath [dotPos] == '.'; } return false; } File File::withFileExtension (StringRef newExtension) const { if (fullPath.isEmpty()) return File(); String filePart (getFileName()); const int i = filePart.lastIndexOfChar ('.'); if (i >= 0) filePart = filePart.substring (0, i); if (newExtension.isNotEmpty() && newExtension.text[0] != '.') filePart << '.'; return getSiblingFile (filePart + newExtension); } //============================================================================== bool File::startAsProcess (const String& parameters) const { return exists() && Process::openDocument (fullPath, parameters); } //============================================================================== FileInputStream* File::createInputStream() const { ScopedPointer fin (new FileInputStream (*this)); if (fin->openedOk()) return fin.release(); return nullptr; } FileOutputStream* File::createOutputStream (const size_t bufferSize) const { ScopedPointer out (new FileOutputStream (*this, bufferSize)); return out->failedToOpen() ? nullptr : out.release(); } //============================================================================== bool File::appendData (const void* const dataToAppend, const size_t numberOfBytes) const { jassert (((ssize_t) numberOfBytes) >= 0); if (numberOfBytes == 0) return true; FileOutputStream out (*this, 8192); return out.openedOk() && out.write (dataToAppend, numberOfBytes); } bool File::replaceWithData (const void* const dataToWrite, const size_t numberOfBytes) const { if (numberOfBytes == 0) return deleteFile(); TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile); tempFile.getFile().appendData (dataToWrite, numberOfBytes); return tempFile.overwriteTargetFileWithTemporary(); } bool File::appendText (const String& text, const bool asUnicode, const bool writeUnicodeHeaderBytes) const { FileOutputStream out (*this); if (out.failedToOpen()) return false; out.writeText (text, asUnicode, writeUnicodeHeaderBytes); return true; } bool File::replaceWithText (const String& textToWrite, const bool asUnicode, const bool writeUnicodeHeaderBytes) const { TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile); tempFile.getFile().appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes); return tempFile.overwriteTargetFileWithTemporary(); } bool File::hasIdenticalContentTo (const File& other) const { if (other == *this) return true; if (getSize() == other.getSize() && existsAsFile() && other.existsAsFile()) { FileInputStream in1 (*this), in2 (other); if (in1.openedOk() && in2.openedOk()) { const int bufferSize = 4096; HeapBlock buffer1 (bufferSize), buffer2 (bufferSize); for (;;) { const int num1 = in1.read (buffer1, bufferSize); const int num2 = in2.read (buffer2, bufferSize); if (num1 != num2) break; if (num1 <= 0) return true; if (memcmp (buffer1, buffer2, (size_t) num1) != 0) break; } } } return false; } //============================================================================== String File::createLegalPathName (const String& original) { String s (original); String start; if (s.isNotEmpty() && s[1] == ':') { start = s.substring (0, 2); s = s.substring (2); } return start + s.removeCharacters ("\"#@,;:<>*^|?") .substring (0, 1024); } String File::createLegalFileName (const String& original) { String s (original.removeCharacters ("\"#@,;:<>*^|?\\/")); const int maxLength = 128; // only the length of the filename, not the whole path const int len = s.length(); if (len > maxLength) { const int lastDot = s.lastIndexOfChar ('.'); if (lastDot > jmax (0, len - 12)) { s = s.substring (0, maxLength - (len - lastDot)) + s.substring (lastDot); } else { s = s.substring (0, maxLength); } } return s; } //============================================================================== static int countNumberOfSeparators (String::CharPointerType s) { int num = 0; for (;;) { const juce_wchar c = s.getAndAdvance(); if (c == 0) break; if (c == File::separator) ++num; } return num; } String File::getRelativePathFrom (const File& dir) const { String thisPath (fullPath); while (thisPath.endsWithChar (separator)) thisPath = thisPath.dropLastCharacters (1); String dirPath (addTrailingSeparator (dir.existsAsFile() ? dir.getParentDirectory().getFullPathName() : dir.fullPath)); int commonBitLength = 0; String::CharPointerType thisPathAfterCommon (thisPath.getCharPointer()); String::CharPointerType dirPathAfterCommon (dirPath.getCharPointer()); { String::CharPointerType thisPathIter (thisPath.getCharPointer()); String::CharPointerType dirPathIter (dirPath.getCharPointer()); for (int i = 0;;) { const juce_wchar c1 = thisPathIter.getAndAdvance(); const juce_wchar c2 = dirPathIter.getAndAdvance(); #if NAMES_ARE_CASE_SENSITIVE if (c1 != c2 #else if ((c1 != c2 && CharacterFunctions::toLowerCase (c1) != CharacterFunctions::toLowerCase (c2)) #endif || c1 == 0) break; ++i; if (c1 == separator) { thisPathAfterCommon = thisPathIter; dirPathAfterCommon = dirPathIter; commonBitLength = i; } } } // if the only common bit is the root, then just return the full path.. if (commonBitLength == 0 || (commonBitLength == 1 && thisPath[1] == separator)) return fullPath; const int numUpDirectoriesNeeded = countNumberOfSeparators (dirPathAfterCommon); if (numUpDirectoriesNeeded == 0) return thisPathAfterCommon; #if JUCE_WINDOWS String s (String::repeatedString ("..\\", numUpDirectoriesNeeded)); #else String s (String::repeatedString ("../", numUpDirectoriesNeeded)); #endif s.appendCharPointer (thisPathAfterCommon); return s; } //============================================================================== File File::createTempFile (StringRef fileNameEnding) { const File tempFile (getSpecialLocation (tempDirectory) .getChildFile ("temp_" + String::toHexString (Random::getSystemRandom().nextInt())) .withFileExtension (fileNameEnding)); if (tempFile.exists()) return createTempFile (fileNameEnding); return tempFile; } //============================================================================== MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode) : address (nullptr), range (0, file.getSize()), fileHandle (0) { openInternal (file, mode); } MemoryMappedFile::MemoryMappedFile (const File& file, const Range& fileRange, AccessMode mode) : address (nullptr), range (fileRange.getIntersectionWith (Range (0, file.getSize()))), fileHandle (0) { openInternal (file, mode); } //============================================================================== #if JUCE_UNIT_TESTS class FileTests : public UnitTest { public: FileTests() : UnitTest ("Files") {} void runTest() { beginTest ("Reading"); const File home (File::getSpecialLocation (File::userHomeDirectory)); const File temp (File::getSpecialLocation (File::tempDirectory)); expect (! File::nonexistent.exists()); expect (! File::nonexistent.existsAsFile()); expect (! File::nonexistent.isDirectory()); #if ! JUCE_WINDOWS expect (File("/").isDirectory()); #endif expect (home.isDirectory()); expect (home.exists()); expect (! home.existsAsFile()); expect (File::getSpecialLocation (File::userDocumentsDirectory).isDirectory()); expect (File::getSpecialLocation (File::userApplicationDataDirectory).isDirectory()); expect (File::getSpecialLocation (File::currentExecutableFile).exists()); expect (File::getSpecialLocation (File::currentApplicationFile).exists()); expect (File::getSpecialLocation (File::invokedExecutableFile).exists()); expect (home.getVolumeTotalSize() > 1024 * 1024); expect (home.getBytesFreeOnVolume() > 0); expect (! home.isHidden()); expect (home.isOnHardDisk()); expect (! home.isOnCDRomDrive()); expect (File::getCurrentWorkingDirectory().exists()); expect (home.setAsCurrentWorkingDirectory()); expect (File::getCurrentWorkingDirectory() == home); { Array roots; File::findFileSystemRoots (roots); expect (roots.size() > 0); int numRootsExisting = 0; for (int i = 0; i < roots.size(); ++i) if (roots[i].exists()) ++numRootsExisting; // (on windows, some of the drives may not contain media, so as long as at least one is ok..) expect (numRootsExisting > 0); } beginTest ("Writing"); File demoFolder (temp.getChildFile ("Juce UnitTests Temp Folder.folder")); expect (demoFolder.deleteRecursively()); expect (demoFolder.createDirectory()); expect (demoFolder.isDirectory()); expect (demoFolder.getParentDirectory() == temp); expect (temp.isDirectory()); { Array files; temp.findChildFiles (files, File::findFilesAndDirectories, false, "*"); expect (files.contains (demoFolder)); } { Array files; temp.findChildFiles (files, File::findDirectories, true, "*.folder"); expect (files.contains (demoFolder)); } File tempFile (demoFolder.getNonexistentChildFile ("test", ".txt", false)); expect (tempFile.getFileExtension() == ".txt"); expect (tempFile.hasFileExtension (".txt")); expect (tempFile.hasFileExtension ("txt")); expect (tempFile.withFileExtension ("xyz").hasFileExtension (".xyz")); expect (tempFile.withFileExtension ("xyz").hasFileExtension ("abc;xyz;foo")); expect (tempFile.withFileExtension ("xyz").hasFileExtension ("xyz;foo")); expect (! tempFile.withFileExtension ("h").hasFileExtension ("bar;foo;xx")); expect (tempFile.getSiblingFile ("foo").isAChildOf (temp)); expect (tempFile.hasWriteAccess()); { FileOutputStream fo (tempFile); fo.write ("0123456789", 10); } expect (tempFile.exists()); expect (tempFile.getSize() == 10); expect (std::abs ((int) (tempFile.getLastModificationTime().toMilliseconds() - Time::getCurrentTime().toMilliseconds())) < 3000); expectEquals (tempFile.loadFileAsString(), String ("0123456789")); expect (! demoFolder.containsSubDirectories()); expectEquals (tempFile.getRelativePathFrom (demoFolder.getParentDirectory()), demoFolder.getFileName() + File::separatorString + tempFile.getFileName()); expectEquals (demoFolder.getParentDirectory().getRelativePathFrom (tempFile), ".." + File::separatorString + ".." + File::separatorString + demoFolder.getParentDirectory().getFileName()); expect (demoFolder.getNumberOfChildFiles (File::findFiles) == 1); expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 1); expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 0); demoFolder.getNonexistentChildFile ("tempFolder", "", false).createDirectory(); expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 1); expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 2); expect (demoFolder.containsSubDirectories()); expect (tempFile.hasWriteAccess()); tempFile.setReadOnly (true); expect (! tempFile.hasWriteAccess()); tempFile.setReadOnly (false); expect (tempFile.hasWriteAccess()); Time t (Time::getCurrentTime()); tempFile.setLastModificationTime (t); Time t2 = tempFile.getLastModificationTime(); expect (std::abs ((int) (t2.toMilliseconds() - t.toMilliseconds())) <= 1000); { MemoryBlock mb; tempFile.loadFileAsData (mb); expect (mb.getSize() == 10); expect (mb[0] == '0'); } { expect (tempFile.getSize() == 10); FileOutputStream fo (tempFile); expect (fo.openedOk()); expect (fo.setPosition (7)); expect (fo.truncate().wasOk()); expect (tempFile.getSize() == 7); fo.write ("789", 3); fo.flush(); expect (tempFile.getSize() == 10); } beginTest ("Memory-mapped files"); { MemoryMappedFile mmf (tempFile, MemoryMappedFile::readOnly); expect (mmf.getSize() == 10); expect (mmf.getData() != nullptr); expect (memcmp (mmf.getData(), "0123456789", 10) == 0); } { const File tempFile2 (tempFile.getNonexistentSibling (false)); expect (tempFile2.create()); expect (tempFile2.appendData ("xxxxxxxxxx", 10)); { MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite); expect (mmf.getSize() == 10); expect (mmf.getData() != nullptr); memcpy (mmf.getData(), "abcdefghij", 10); } { MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite); expect (mmf.getSize() == 10); expect (mmf.getData() != nullptr); expect (memcmp (mmf.getData(), "abcdefghij", 10) == 0); } expect (tempFile2.deleteFile()); } beginTest ("More writing"); expect (tempFile.appendData ("abcdefghij", 10)); expect (tempFile.getSize() == 20); expect (tempFile.replaceWithData ("abcdefghij", 10)); expect (tempFile.getSize() == 10); File tempFile2 (tempFile.getNonexistentSibling (false)); expect (tempFile.copyFileTo (tempFile2)); expect (tempFile2.exists()); expect (tempFile2.hasIdenticalContentTo (tempFile)); expect (tempFile.deleteFile()); expect (! tempFile.exists()); expect (tempFile2.moveFileTo (tempFile)); expect (tempFile.exists()); expect (! tempFile2.exists()); expect (demoFolder.deleteRecursively()); expect (! demoFolder.exists()); } }; static FileTests fileUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_File.h000066400000000000000000001220171320201440200267050ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_FILE_H_INCLUDED #define JUCE_FILE_H_INCLUDED //============================================================================== /** Represents a local file or directory. This class encapsulates the absolute pathname of a file or directory, and has methods for finding out about the file and changing its properties. To read or write to the file, there are methods for returning an input or output stream. @see FileInputStream, FileOutputStream */ class JUCE_API File { public: //============================================================================== /** Creates an (invalid) file object. The file is initially set to an empty path, so getFullPath() will return an empty string, and comparing the file to File::nonexistent will return true. You can use its operator= method to point it at a proper file. */ File() noexcept {} /** Creates a file from an absolute path. If the path supplied is a relative path, it is taken to be relative to the current working directory (see File::getCurrentWorkingDirectory()), but this isn't a recommended way of creating a file, because you never know what the CWD is going to be. On the Mac/Linux, the path can include "~" notation for referring to user home directories. */ File (const String& absolutePath); /** Creates a copy of another file object. */ File (const File&); /** Destructor. */ ~File() noexcept {} /** Sets the file based on an absolute pathname. If the path supplied is a relative path, it is taken to be relative to the current working directory (see File::getCurrentWorkingDirectory()), but this isn't a recommended way of creating a file, because you never know what the CWD is going to be. On the Mac/Linux, the path can include "~" notation for referring to user home directories. */ File& operator= (const String& newAbsolutePath); /** Copies from another file object. */ File& operator= (const File& otherFile); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS File (File&&) noexcept; File& operator= (File&&) noexcept; #endif //============================================================================== /** This static constant is used for referring to an 'invalid' file. */ static const File nonexistent; //============================================================================== /** Checks whether the file actually exists. @returns true if the file exists, either as a file or a directory. @see existsAsFile, isDirectory */ bool exists() const; /** Checks whether the file exists and is a file rather than a directory. @returns true only if this is a real file, false if it's a directory or doesn't exist @see exists, isDirectory */ bool existsAsFile() const; /** Checks whether the file is a directory that exists. @returns true only if the file is a directory which actually exists, so false if it's a file or doesn't exist at all @see exists, existsAsFile */ bool isDirectory() const; /** Returns the size of the file in bytes. @returns the number of bytes in the file, or 0 if it doesn't exist. */ int64 getSize() const; /** Utility function to convert a file size in bytes to a neat string description. So for example 100 would return "100 bytes", 2000 would return "2 KB", 2000000 would produce "2 MB", etc. */ static String descriptionOfSizeInBytes (int64 bytes); //============================================================================== /** Returns the complete, absolute path of this file. This includes the filename and all its parent folders. On Windows it'll also include the drive letter prefix; on Mac or Linux it'll be a complete path starting from the root folder. If you just want the file's name, you should use getFileName() or getFileNameWithoutExtension(). @see getFileName, getRelativePathFrom */ const String& getFullPathName() const noexcept { return fullPath; } /** Returns the last section of the pathname. Returns just the final part of the path - e.g. if the whole path is "/moose/fish/foo.txt" this will return "foo.txt". For a directory, it returns the final part of the path - e.g. for the directory "/moose/fish" it'll return "fish". If the filename begins with a dot, it'll return the whole filename, e.g. for "/moose/.fish", it'll return ".fish" @see getFullPathName, getFileNameWithoutExtension */ String getFileName() const; /** Creates a relative path that refers to a file relatively to a given directory. e.g. File ("/moose/foo.txt").getRelativePathFrom (File ("/moose/fish/haddock")) would return "../../foo.txt". If it's not possible to navigate from one file to the other, an absolute path is returned. If the paths are invalid, an empty string may also be returned. @param directoryToBeRelativeTo the directory which the resultant string will be relative to. If this is actually a file rather than a directory, its parent directory will be used instead. If it doesn't exist, it's assumed to be a directory. @see getChildFile, isAbsolutePath */ String getRelativePathFrom (const File& directoryToBeRelativeTo) const; //============================================================================== /** Returns the file's extension. Returns the file extension of this file, also including the dot. e.g. "/moose/fish/foo.txt" would return ".txt" @see hasFileExtension, withFileExtension, getFileNameWithoutExtension */ String getFileExtension() const; /** Checks whether the file has a given extension. @param extensionToTest the extension to look for - it doesn't matter whether or not this string has a dot at the start, so ".wav" and "wav" will have the same effect. To compare with multiple extensions, this parameter can contain multiple strings, separated by semi-colons - so, for example: hasFileExtension (".jpeg;png;gif") would return true if the file has any of those three extensions. @see getFileExtension, withFileExtension, getFileNameWithoutExtension */ bool hasFileExtension (StringRef extensionToTest) const; /** Returns a version of this file with a different file extension. e.g. File ("/moose/fish/foo.txt").withFileExtension ("html") returns "/moose/fish/foo.html" @param newExtension the new extension, either with or without a dot at the start (this doesn't make any difference). To get remove a file's extension altogether, pass an empty string into this function. @see getFileName, getFileExtension, hasFileExtension, getFileNameWithoutExtension */ File withFileExtension (StringRef newExtension) const; /** Returns the last part of the filename, without its file extension. e.g. for "/moose/fish/foo.txt" this will return "foo". @see getFileName, getFileExtension, hasFileExtension, withFileExtension */ String getFileNameWithoutExtension() const; //============================================================================== /** Returns a 32-bit hash-code that identifies this file. This is based on the filename. Obviously it's possible, although unlikely, that two files will have the same hash-code. */ int hashCode() const; /** Returns a 64-bit hash-code that identifies this file. This is based on the filename. Obviously it's possible, although unlikely, that two files will have the same hash-code. */ int64 hashCode64() const; //============================================================================== /** Returns a file that represents a relative (or absolute) sub-path of the current one. This will find a child file or directory of the current object. e.g. File ("/moose/fish").getChildFile ("foo.txt") will produce "/moose/fish/foo.txt". File ("/moose/fish").getChildFile ("haddock/foo.txt") will produce "/moose/fish/haddock/foo.txt". File ("/moose/fish").getChildFile ("../foo.txt") will produce "/moose/foo.txt". If the string is actually an absolute path, it will be treated as such, e.g. File ("/moose/fish").getChildFile ("/foo.txt") will produce "/foo.txt" @see getSiblingFile, getParentDirectory, getRelativePathFrom, isAChildOf */ File getChildFile (StringRef relativeOrAbsolutePath) const; /** Returns a file which is in the same directory as this one. This is equivalent to getParentDirectory().getChildFile (name). @see getChildFile, getParentDirectory */ File getSiblingFile (StringRef siblingFileName) const; //============================================================================== /** Returns the directory that contains this file or directory. e.g. for "/moose/fish/foo.txt" this will return "/moose/fish". */ File getParentDirectory() const; /** Checks whether a file is somewhere inside a directory. Returns true if this file is somewhere inside a subdirectory of the directory that is passed in. Neither file actually has to exist, because the function just checks the paths for similarities. e.g. File ("/moose/fish/foo.txt").isAChildOf ("/moose") is true. File ("/moose/fish/foo.txt").isAChildOf ("/moose/fish") is also true. */ bool isAChildOf (const File& potentialParentDirectory) const; //============================================================================== /** Chooses a filename relative to this one that doesn't already exist. If this file is a directory, this will return a child file of this directory that doesn't exist, by adding numbers to a prefix and suffix until it finds one that isn't already there. If the prefix + the suffix doesn't exist, it won't bother adding a number. e.g. File ("/moose/fish").getNonexistentChildFile ("foo", ".txt", true) might return "/moose/fish/foo(2).txt" if there's already a file called "foo.txt". @param prefix the string to use for the filename before the number @param suffix the string to add to the filename after the number @param putNumbersInBrackets if true, this will create filenames in the format "prefix(number)suffix", if false, it will leave the brackets out. */ File getNonexistentChildFile (const String& prefix, const String& suffix, bool putNumbersInBrackets = true) const; /** Chooses a filename for a sibling file to this one that doesn't already exist. If this file doesn't exist, this will just return itself, otherwise it will return an appropriate sibling that doesn't exist, e.g. if a file "/moose/fish/foo.txt" exists, this might return "/moose/fish/foo(2).txt". @param putNumbersInBrackets whether to add brackets around the numbers that get appended to the new filename. */ File getNonexistentSibling (bool putNumbersInBrackets = true) const; //============================================================================== /** Compares the pathnames for two files. */ bool operator== (const File&) const; /** Compares the pathnames for two files. */ bool operator!= (const File&) const; /** Compares the pathnames for two files. */ bool operator< (const File&) const; /** Compares the pathnames for two files. */ bool operator> (const File&) const; //============================================================================== /** Checks whether a file can be created or written to. @returns true if it's possible to create and write to this file. If the file doesn't already exist, this will check its parent directory to see if writing is allowed. @see setReadOnly */ bool hasWriteAccess() const; /** Changes the write-permission of a file or directory. @param shouldBeReadOnly whether to add or remove write-permission @param applyRecursively if the file is a directory and this is true, it will recurse through all the subfolders changing the permissions of all files @returns true if it manages to change the file's permissions. @see hasWriteAccess */ bool setReadOnly (bool shouldBeReadOnly, bool applyRecursively = false) const; /** Changes the execute-permissions of a file. @param shouldBeExecutable whether to add or remove execute-permission @returns true if it manages to change the file's permissions. */ bool setExecutePermission (bool shouldBeExecutable) const; /** Returns true if this file is a hidden or system file. The criteria for deciding whether a file is hidden are platform-dependent. */ bool isHidden() const; /** Returns true if this file is a link or alias that can be followed using getLinkedTarget(). */ bool isLink() const; /** If this file is a link or alias, this returns the file that it points to. If the file isn't actually link, it'll just return itself. */ File getLinkedTarget() const; /** Returns a unique identifier for the file, if one is available. Depending on the OS and file-system, this may be a unix inode number or a win32 file identifier, or 0 if it fails to find one. The number will be unique on the filesystem, but not globally. */ uint64 getFileIdentifier() const; //============================================================================== /** Returns the last modification time of this file. @returns the time, or an invalid time if the file doesn't exist. @see setLastModificationTime, getLastAccessTime, getCreationTime */ Time getLastModificationTime() const; /** Returns the last time this file was accessed. @returns the time, or an invalid time if the file doesn't exist. @see setLastAccessTime, getLastModificationTime, getCreationTime */ Time getLastAccessTime() const; /** Returns the time that this file was created. @returns the time, or an invalid time if the file doesn't exist. @see getLastModificationTime, getLastAccessTime */ Time getCreationTime() const; /** Changes the modification time for this file. @param newTime the time to apply to the file @returns true if it manages to change the file's time. @see getLastModificationTime, setLastAccessTime, setCreationTime */ bool setLastModificationTime (Time newTime) const; /** Changes the last-access time for this file. @param newTime the time to apply to the file @returns true if it manages to change the file's time. @see getLastAccessTime, setLastModificationTime, setCreationTime */ bool setLastAccessTime (Time newTime) const; /** Changes the creation date for this file. @param newTime the time to apply to the file @returns true if it manages to change the file's time. @see getCreationTime, setLastModificationTime, setLastAccessTime */ bool setCreationTime (Time newTime) const; /** If possible, this will try to create a version string for the given file. The OS may be able to look at the file and give a version for it - e.g. with executables, bundles, dlls, etc. If no version is available, this will return an empty string. */ String getVersion() const; //============================================================================== /** Creates an empty file if it doesn't already exist. If the file that this object refers to doesn't exist, this will create a file of zero size. If it already exists or is a directory, this method will do nothing. @returns true if the file has been created (or if it already existed). @see createDirectory */ Result create() const; /** Creates a new directory for this filename. This will try to create the file as a directory, and fill also create any parent directories it needs in order to complete the operation. @returns a result to indicate whether the directory was created successfully, or an error message if it failed. @see create */ Result createDirectory() const; /** Deletes a file. If this file is actually a directory, it may not be deleted correctly if it contains files. See deleteRecursively() as a better way of deleting directories. @returns true if the file has been successfully deleted (or if it didn't exist to begin with). @see deleteRecursively */ bool deleteFile() const; /** Deletes a file or directory and all its subdirectories. If this file is a directory, this will try to delete it and all its subfolders. If it's just a file, it will just try to delete the file. @returns true if the file and all its subfolders have been successfully deleted (or if it didn't exist to begin with). @see deleteFile */ bool deleteRecursively() const; /** Moves this file or folder to the trash. @returns true if the operation succeeded. It could fail if the trash is full, or if the file is write-protected, so you should check the return value and act appropriately. */ bool moveToTrash() const; /** Moves or renames a file. Tries to move a file to a different location. If the target file already exists, this will attempt to delete it first, and will fail if this can't be done. Note that the destination file isn't the directory to put it in, it's the actual filename that you want the new file to have. Also note that on some OSes (e.g. Windows), moving files between different volumes may not be possible. @returns true if the operation succeeds */ bool moveFileTo (const File& targetLocation) const; /** Copies a file. Tries to copy a file to a different location. If the target file already exists, this will attempt to delete it first, and will fail if this can't be done. @returns true if the operation succeeds */ bool copyFileTo (const File& targetLocation) const; /** Copies a directory. Tries to copy an entire directory, recursively. If this file isn't a directory or if any target files can't be created, this will return false. @param newDirectory the directory that this one should be copied to. Note that this is the name of the actual directory to create, not the directory into which the new one should be placed, so there must be enough write privileges to create it if it doesn't exist. Any files inside it will be overwritten by similarly named ones that are copied. */ bool copyDirectoryTo (const File& newDirectory) const; //============================================================================== /** Used in file searching, to specify whether to return files, directories, or both. */ enum TypesOfFileToFind { findDirectories = 1, /**< Use this flag to indicate that you want to find directories. */ findFiles = 2, /**< Use this flag to indicate that you want to find files. */ findFilesAndDirectories = 3, /**< Use this flag to indicate that you want to find both files and directories. */ ignoreHiddenFiles = 4 /**< Add this flag to avoid returning any hidden files in the results. */ }; /** Searches inside a directory for files matching a wildcard pattern. Assuming that this file is a directory, this method will search it for either files or subdirectories whose names match a filename pattern. @param results an array to which File objects will be added for the files that the search comes up with @param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to return files, directories, or both. If the ignoreHiddenFiles flag is also added to this value, hidden files won't be returned @param searchRecursively if true, all subdirectories will be recursed into to do an exhaustive search @param wildCardPattern the filename pattern to search for, e.g. "*.txt" @returns the number of results that have been found @see getNumberOfChildFiles, DirectoryIterator */ int findChildFiles (Array& results, int whatToLookFor, bool searchRecursively, const String& wildCardPattern = "*") const; /** Searches inside a directory and counts how many files match a wildcard pattern. Assuming that this file is a directory, this method will search it for either files or subdirectories whose names match a filename pattern, and will return the number of matches found. This isn't a recursive call, and will only search this directory, not its children. @param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to count files, directories, or both. If the ignoreHiddenFiles flag is also added to this value, hidden files won't be counted @param wildCardPattern the filename pattern to search for, e.g. "*.txt" @returns the number of matches found @see findChildFiles, DirectoryIterator */ int getNumberOfChildFiles (int whatToLookFor, const String& wildCardPattern = "*") const; /** Returns true if this file is a directory that contains one or more subdirectories. @see isDirectory, findChildFiles */ bool containsSubDirectories() const; //============================================================================== /** Creates a stream to read from this file. @returns a stream that will read from this file (initially positioned at the start of the file), or nullptr if the file can't be opened for some reason @see createOutputStream, loadFileAsData */ FileInputStream* createInputStream() const; /** Creates a stream to write to this file. If the file exists, the stream that is returned will be positioned ready for writing at the end of the file, so you might want to use deleteFile() first to write to an empty file. @returns a stream that will write to this file (initially positioned at the end of the file), or nullptr if the file can't be opened for some reason @see createInputStream, appendData, appendText */ FileOutputStream* createOutputStream (size_t bufferSize = 0x8000) const; //============================================================================== /** Loads a file's contents into memory as a block of binary data. Of course, trying to load a very large file into memory will blow up, so it's better to check first. @param result the data block to which the file's contents should be appended - note that if the memory block might already contain some data, you might want to clear it first @returns true if the file could all be read into memory */ bool loadFileAsData (MemoryBlock& result) const; /** Reads a file into memory as a string. Attempts to load the entire file as a zero-terminated string. This makes use of InputStream::readEntireStreamAsString, which can read either UTF-16 or UTF-8 file formats. */ String loadFileAsString() const; /** Reads the contents of this file as text and splits it into lines, which are appended to the given StringArray. */ void readLines (StringArray& destLines) const; //============================================================================== /** Appends a block of binary data to the end of the file. This will try to write the given buffer to the end of the file. @returns false if it can't write to the file for some reason */ bool appendData (const void* dataToAppend, size_t numberOfBytes) const; /** Replaces this file's contents with a given block of data. This will delete the file and replace it with the given data. A nice feature of this method is that it's safe - instead of deleting the file first and then re-writing it, it creates a new temporary file, writes the data to that, and then moves the new file to replace the existing file. This means that if the power gets pulled out or something crashes, you're a lot less likely to end up with a corrupted or unfinished file.. Returns true if the operation succeeds, or false if it fails. @see appendText */ bool replaceWithData (const void* dataToWrite, size_t numberOfBytes) const; /** Appends a string to the end of the file. This will try to append a text string to the file, as either 16-bit unicode or 8-bit characters in the default system encoding. It can also write the 'ff fe' unicode header bytes before the text to indicate the endianness of the file. Any single \\n characters in the string are replaced with \\r\\n before it is written. @see replaceWithText */ bool appendText (const String& textToAppend, bool asUnicode = false, bool writeUnicodeHeaderBytes = false) const; /** Replaces this file's contents with a given text string. This will delete the file and replace it with the given text. A nice feature of this method is that it's safe - instead of deleting the file first and then re-writing it, it creates a new temporary file, writes the text to that, and then moves the new file to replace the existing file. This means that if the power gets pulled out or something crashes, you're a lot less likely to end up with an empty file.. For an explanation of the parameters here, see the appendText() method. Returns true if the operation succeeds, or false if it fails. @see appendText */ bool replaceWithText (const String& textToWrite, bool asUnicode = false, bool writeUnicodeHeaderBytes = false) const; /** Attempts to scan the contents of this file and compare it to another file, returning true if this is possible and they match byte-for-byte. */ bool hasIdenticalContentTo (const File& other) const; //============================================================================== /** Creates a set of files to represent each file root. e.g. on Windows this will create files for "c:\", "d:\" etc according to which ones are available. On the Mac/Linux, this will probably just add a single entry for "/". */ static void findFileSystemRoots (Array& results); /** Finds the name of the drive on which this file lives. @returns the volume label of the drive, or an empty string if this isn't possible */ String getVolumeLabel() const; /** Returns the serial number of the volume on which this file lives. @returns the serial number, or zero if there's a problem doing this */ int getVolumeSerialNumber() const; /** Returns the number of bytes free on the drive that this file lives on. @returns the number of bytes free, or 0 if there's a problem finding this out @see getVolumeTotalSize */ int64 getBytesFreeOnVolume() const; /** Returns the total size of the drive that contains this file. @returns the total number of bytes that the volume can hold @see getBytesFreeOnVolume */ int64 getVolumeTotalSize() const; /** Returns true if this file is on a CD or DVD drive. */ bool isOnCDRomDrive() const; /** Returns true if this file is on a hard disk. This will fail if it's a network drive, but will still be true for removable hard-disks. */ bool isOnHardDisk() const; /** Returns true if this file is on a removable disk drive. This might be a usb-drive, a CD-rom, or maybe a network drive. */ bool isOnRemovableDrive() const; //============================================================================== /** Launches the file as a process. - if the file is executable, this will run it. - if it's a document of some kind, it will launch the document with its default viewer application. - if it's a folder, it will be opened in Explorer, Finder, or equivalent. @see revealToUser */ bool startAsProcess (const String& parameters = String()) const; /** Opens Finder, Explorer, or whatever the OS uses, to show the user this file's location. @see startAsProcess */ void revealToUser() const; //============================================================================== /** A set of types of location that can be passed to the getSpecialLocation() method. */ enum SpecialLocationType { /** The user's home folder. This is the same as using File ("~"). */ userHomeDirectory, /** The user's default documents folder. On Windows, this might be the user's "My Documents" folder. On the Mac it'll be their "Documents" folder. Linux doesn't tend to have one of these, so it might just return their home folder. */ userDocumentsDirectory, /** The folder that contains the user's desktop objects. */ userDesktopDirectory, /** The most likely place where a user might store their music files. */ userMusicDirectory, /** The most likely place where a user might store their movie files. */ userMoviesDirectory, /** The most likely place where a user might store their picture files. */ userPicturesDirectory, /** The folder in which applications store their persistent user-specific settings. On Windows, this might be "\Documents and Settings\username\Application Data". On the Mac, it might be "~/Library". If you're going to store your settings in here, always create your own sub-folder to put them in, to avoid making a mess. */ userApplicationDataDirectory, /** An equivalent of the userApplicationDataDirectory folder that is shared by all users of the computer, rather than just the current user. On the Mac it'll be "/Library", on Windows, it could be something like "\Documents and Settings\All Users\Application Data". Depending on the setup, this folder may be read-only. */ commonApplicationDataDirectory, /** A place to put documents which are shared by all users of the machine. On Windows this may be somewhere like "C:\Users\Public\Documents", on OSX it will be something like "/Users/Shared". Other OSes may have no such concept though, so be careful. */ commonDocumentsDirectory, /** The folder that should be used for temporary files. Always delete them when you're finished, to keep the user's computer tidy! */ tempDirectory, /** Returns this application's executable file. If running as a plug-in or DLL, this will (where possible) be the DLL rather than the host app. On the mac this will return the unix binary, not the package folder - see currentApplicationFile for that. See also invokedExecutableFile, which is similar, but if the exe was launched from a file link, invokedExecutableFile will return the name of the link. */ currentExecutableFile, /** Returns this application's location. If running as a plug-in or DLL, this will (where possible) be the DLL rather than the host app. On the mac this will return the package folder (if it's in one), not the unix binary that's inside it - compare with currentExecutableFile. */ currentApplicationFile, /** Returns the file that was invoked to launch this executable. This may differ from currentExecutableFile if the app was started from e.g. a link - this will return the name of the link that was used, whereas currentExecutableFile will return the actual location of the target executable. */ invokedExecutableFile, /** In a plugin, this will return the path of the host executable. */ hostApplicationPath, #if JUCE_WINDOWS /** On a Windows machine, returns the location of the Windows/System32 folder. */ windowsSystemDirectory, #endif /** The directory in which applications normally get installed. So on windows, this would be something like "c:\program files", on the Mac "/Applications", or "/usr" on linux. */ globalApplicationsDirectory }; /** Finds the location of a special type of file or directory, such as a home folder or documents folder. @see SpecialLocationType */ static File JUCE_CALLTYPE getSpecialLocation (const SpecialLocationType type); //============================================================================== /** Returns a temporary file in the system's temp directory. This will try to return the name of a non-existent temp file. To get the temp folder, you can use getSpecialLocation (File::tempDirectory). */ static File createTempFile (StringRef fileNameEnding); //============================================================================== /** Returns the current working directory. @see setAsCurrentWorkingDirectory */ static File getCurrentWorkingDirectory(); /** Sets the current working directory to be this file. For this to work the file must point to a valid directory. @returns true if the current directory has been changed. @see getCurrentWorkingDirectory */ bool setAsCurrentWorkingDirectory() const; //============================================================================== /** The system-specific file separator character. On Windows, this will be '\', on Mac/Linux, it'll be '/' */ static const juce_wchar separator; /** The system-specific file separator character, as a string. On Windows, this will be '\', on Mac/Linux, it'll be '/' */ static const String separatorString; //============================================================================== /** Returns a version of a filename with any illegal characters removed. This will return a copy of the given string after removing characters that are not allowed in a legal filename, and possibly shortening the string if it's too long. Because this will remove slashes, don't use it on an absolute pathname - use createLegalPathName() for that. @see createLegalPathName */ static String createLegalFileName (const String& fileNameToFix); /** Returns a version of a path with any illegal characters removed. Similar to createLegalFileName(), but this won't remove slashes, so can be used on a complete pathname. @see createLegalFileName */ static String createLegalPathName (const String& pathNameToFix); /** Indicates whether filenames are case-sensitive on the current operating system. */ static bool areFileNamesCaseSensitive(); /** Returns true if the string seems to be a fully-specified absolute path. */ static bool isAbsolutePath (StringRef path); /** Creates a file that simply contains this string, without doing the sanity-checking that the normal constructors do. Best to avoid this unless you really know what you're doing. */ static File createFileWithoutCheckingPath (const String& absolutePath) noexcept; /** Adds a separator character to the end of a path if it doesn't already have one. */ static String addTrailingSeparator (const String& path); #if JUCE_MAC || JUCE_IOS || DOXYGEN //============================================================================== /** OSX ONLY - Finds the OSType of a file from the its resources. */ OSType getMacOSType() const; /** OSX ONLY - Returns true if this file is actually a bundle. */ bool isBundle() const; #endif #if JUCE_MAC || DOXYGEN /** OSX ONLY - Adds this file to the OSX dock */ void addToDock() const; #endif #if JUCE_WINDOWS /** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */ bool createLink (const String& description, const File& linkFileToCreate) const; #endif private: //============================================================================== String fullPath; static String parseAbsolutePath (const String&); String getPathUpToLastSlash() const; Result createDirectoryInternal (const String&) const; bool copyInternal (const File&) const; bool moveInternal (const File&) const; bool setFileTimesInternal (int64 m, int64 a, int64 c) const; void getFileTimesInternal (int64& m, int64& a, int64& c) const; bool setFileReadOnlyInternal (bool) const; bool setFileExecutableInternal (bool) const; }; #endif // JUCE_FILE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp000066400000000000000000000031471320201440200304100ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ FileFilter::FileFilter (const String& filterDescription) : description (filterDescription) { } FileFilter::~FileFilter() { } const String& FileFilter::getDescription() const noexcept { return description; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h000066400000000000000000000060211320201440200300470ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_FILEFILTER_H_INCLUDED #define JUCE_FILEFILTER_H_INCLUDED //============================================================================== /** Interface for deciding which files are suitable for something. For example, this is used by DirectoryContentsList to select which files go into the list. @see WildcardFileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent */ class JUCE_API FileFilter { public: //============================================================================== /** Creates a filter with the given description. The description can be returned later with the getDescription() method. */ FileFilter (const String& filterDescription); /** Destructor. */ virtual ~FileFilter(); //============================================================================== /** Returns the description that the filter was created with. */ const String& getDescription() const noexcept; //============================================================================== /** Should return true if this file is suitable for inclusion in whatever context the object is being used. */ virtual bool isFileSuitable (const File& file) const = 0; /** Should return true if this directory is suitable for inclusion in whatever context the object is being used. */ virtual bool isDirectorySuitable (const File& file) const = 0; protected: //============================================================================== String description; }; #endif // JUCE_FILEFILTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp000066400000000000000000000054671320201440200314450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ int64 juce_fileSetPosition (void* handle, int64 pos); //============================================================================== FileInputStream::FileInputStream (const File& f) : file (f), fileHandle (nullptr), currentPosition (0), status (Result::ok()) { openHandle(); } int64 FileInputStream::getTotalLength() { // You should always check that a stream opened successfully before using it! jassert (openedOk()); return file.getSize(); } int FileInputStream::read (void* buffer, int bytesToRead) { // You should always check that a stream opened successfully before using it! jassert (openedOk()); // The buffer should never be null, and a negative size is probably a // sign that something is broken! jassert (buffer != nullptr && bytesToRead >= 0); const size_t num = readInternal (buffer, (size_t) bytesToRead); currentPosition += (int64) num; return (int) num; } bool FileInputStream::isExhausted() { return currentPosition >= getTotalLength(); } int64 FileInputStream::getPosition() { return currentPosition; } bool FileInputStream::setPosition (int64 pos) { // You should always check that a stream opened successfully before using it! jassert (openedOk()); if (pos != currentPosition) currentPosition = juce_fileSetPosition (fileHandle, pos); return currentPosition == pos; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h000066400000000000000000000073631320201440200311070ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_FILEINPUTSTREAM_H_INCLUDED #define JUCE_FILEINPUTSTREAM_H_INCLUDED //============================================================================== /** An input stream that reads from a local file. @see InputStream, FileOutputStream, File::createInputStream */ class JUCE_API FileInputStream : public InputStream { public: //============================================================================== /** Creates a FileInputStream to read from the given file. After creating a FileInputStream, you should use openedOk() or failedToOpen() to make sure that it's OK before trying to read from it! If it failed, you can call getStatus() to get more error information. */ explicit FileInputStream (const File& fileToRead); /** Destructor. */ ~FileInputStream(); //============================================================================== /** Returns the file that this stream is reading from. */ const File& getFile() const noexcept { return file; } /** Returns the status of the file stream. The result will be ok if the file opened successfully. If an error occurs while opening or reading from the file, this will contain an error message. */ const Result& getStatus() const noexcept { return status; } /** Returns true if the stream couldn't be opened for some reason. @see getResult() */ bool failedToOpen() const noexcept { return status.failed(); } /** Returns true if the stream opened without problems. @see getResult() */ bool openedOk() const noexcept { return status.wasOk(); } //============================================================================== int64 getTotalLength() override; int read (void*, int) override; bool isExhausted() override; int64 getPosition() override; bool setPosition (int64) override; private: //============================================================================== const File file; void* fileHandle; int64 currentPosition; Result status; void openHandle(); size_t readInternal (void*, size_t); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream) }; #endif // JUCE_FILEINPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp000066400000000000000000000076171320201440200316450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ int64 juce_fileSetPosition (void* handle, int64 pos); //============================================================================== FileOutputStream::FileOutputStream (const File& f, const size_t bufferSizeToUse) : file (f), fileHandle (nullptr), status (Result::ok()), currentPosition (0), bufferSize (bufferSizeToUse), bytesInBuffer (0), buffer (jmax (bufferSizeToUse, (size_t) 16)) { openHandle(); } FileOutputStream::~FileOutputStream() { flushBuffer(); closeHandle(); } int64 FileOutputStream::getPosition() { return currentPosition; } bool FileOutputStream::setPosition (int64 newPosition) { if (newPosition != currentPosition) { flushBuffer(); currentPosition = juce_fileSetPosition (fileHandle, newPosition); } return newPosition == currentPosition; } bool FileOutputStream::flushBuffer() { bool ok = true; if (bytesInBuffer > 0) { ok = (writeInternal (buffer, bytesInBuffer) == (ssize_t) bytesInBuffer); bytesInBuffer = 0; } return ok; } void FileOutputStream::flush() { flushBuffer(); flushInternal(); } bool FileOutputStream::write (const void* const src, const size_t numBytes) { jassert (src != nullptr && ((ssize_t) numBytes) >= 0); if (bytesInBuffer + numBytes < bufferSize) { memcpy (buffer + bytesInBuffer, src, numBytes); bytesInBuffer += numBytes; currentPosition += (int64) numBytes; } else { if (! flushBuffer()) return false; if (numBytes < bufferSize) { memcpy (buffer + bytesInBuffer, src, numBytes); bytesInBuffer += numBytes; currentPosition += (int64) numBytes; } else { const ssize_t bytesWritten = writeInternal (src, numBytes); if (bytesWritten < 0) return false; currentPosition += (int64) bytesWritten; return bytesWritten == (ssize_t) numBytes; } } return true; } bool FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes) { jassert (((ssize_t) numBytes) >= 0); if (bytesInBuffer + numBytes < bufferSize) { memset (buffer + bytesInBuffer, byte, numBytes); bytesInBuffer += numBytes; currentPosition += (int64) numBytes; return true; } return OutputStream::writeRepeatedByte (byte, numBytes); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h000066400000000000000000000107741320201440200313100ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_FILEOUTPUTSTREAM_H_INCLUDED #define JUCE_FILEOUTPUTSTREAM_H_INCLUDED //============================================================================== /** An output stream that writes into a local file. @see OutputStream, FileInputStream, File::createOutputStream */ class JUCE_API FileOutputStream : public OutputStream { public: //============================================================================== /** Creates a FileOutputStream. If the file doesn't exist, it will first be created. If the file can't be created or opened, the failedToOpen() method will return true. If the file already exists when opened, the stream's write-postion will be set to the end of the file. To overwrite an existing file, use File::deleteFile() before opening the stream, or use setPosition(0) after it's opened (although this won't truncate the file). @see TemporaryFile */ FileOutputStream (const File& fileToWriteTo, size_t bufferSizeToUse = 16384); /** Destructor. */ ~FileOutputStream(); //============================================================================== /** Returns the file that this stream is writing to. */ const File& getFile() const { return file; } /** Returns the status of the file stream. The result will be ok if the file opened successfully. If an error occurs while opening or writing to the file, this will contain an error message. */ const Result& getStatus() const noexcept { return status; } /** Returns true if the stream couldn't be opened for some reason. @see getResult() */ bool failedToOpen() const noexcept { return status.failed(); } /** Returns true if the stream opened without problems. @see getResult() */ bool openedOk() const noexcept { return status.wasOk(); } /** Attempts to truncate the file to the current write position. To truncate a file to a specific size, first use setPosition() to seek to the appropriate location, and then call this method. */ Result truncate(); //============================================================================== void flush() override; int64 getPosition() override; bool setPosition (int64) override; bool write (const void*, size_t) override; bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override; private: //============================================================================== File file; void* fileHandle; Result status; int64 currentPosition; size_t bufferSize, bytesInBuffer; HeapBlock buffer; void openHandle(); void closeHandle(); void flushInternal(); bool flushBuffer(); int64 setPositionInternal (int64); ssize_t writeInternal (const void*, size_t); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileOutputStream) }; #endif // JUCE_FILEOUTPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp000066400000000000000000000117061320201440200312050ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ FileSearchPath::FileSearchPath() {} FileSearchPath::~FileSearchPath() {} FileSearchPath::FileSearchPath (const String& path) { init (path); } FileSearchPath::FileSearchPath (const FileSearchPath& other) : directories (other.directories) { } FileSearchPath& FileSearchPath::operator= (const FileSearchPath& other) { directories = other.directories; return *this; } FileSearchPath& FileSearchPath::operator= (const String& path) { init (path); return *this; } void FileSearchPath::init (const String& path) { directories.clear(); directories.addTokens (path, ";", "\""); directories.trim(); directories.removeEmptyStrings(); for (int i = directories.size(); --i >= 0;) directories.set (i, directories[i].unquoted()); } int FileSearchPath::getNumPaths() const { return directories.size(); } File FileSearchPath::operator[] (const int index) const { return File (directories [index]); } String FileSearchPath::toString() const { StringArray directories2 (directories); for (int i = directories2.size(); --i >= 0;) if (directories2[i].containsChar (';')) directories2.set (i, directories2[i].quoted()); return directories2.joinIntoString (";"); } void FileSearchPath::add (const File& dir, const int insertIndex) { directories.insert (insertIndex, dir.getFullPathName()); } void FileSearchPath::addIfNotAlreadyThere (const File& dir) { for (int i = 0; i < directories.size(); ++i) if (File (directories[i]) == dir) return; add (dir); } void FileSearchPath::remove (const int index) { directories.remove (index); } void FileSearchPath::addPath (const FileSearchPath& other) { for (int i = 0; i < other.getNumPaths(); ++i) addIfNotAlreadyThere (other[i]); } void FileSearchPath::removeRedundantPaths() { for (int i = directories.size(); --i >= 0;) { const File d1 (directories[i]); for (int j = directories.size(); --j >= 0;) { const File d2 (directories[j]); if ((i != j) && (d1.isAChildOf (d2) || d1 == d2)) { directories.remove (i); break; } } } } void FileSearchPath::removeNonExistentPaths() { for (int i = directories.size(); --i >= 0;) if (! File (directories[i]).isDirectory()) directories.remove (i); } int FileSearchPath::findChildFiles (Array& results, const int whatToLookFor, const bool searchRecursively, const String& wildCardPattern) const { int total = 0; for (int i = 0; i < directories.size(); ++i) total += operator[] (i).findChildFiles (results, whatToLookFor, searchRecursively, wildCardPattern); return total; } bool FileSearchPath::isFileInPath (const File& fileToCheck, const bool checkRecursively) const { for (int i = directories.size(); --i >= 0;) { const File d (directories[i]); if (checkRecursively) { if (fileToCheck.isAChildOf (d)) return true; } else { if (fileToCheck.getParentDirectory() == d) return true; } } return false; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h000066400000000000000000000153351320201440200306540ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_FILESEARCHPATH_H_INCLUDED #define JUCE_FILESEARCHPATH_H_INCLUDED //============================================================================== /** Represents a set of folders that make up a search path. @see File */ class JUCE_API FileSearchPath { public: //============================================================================== /** Creates an empty search path. */ FileSearchPath(); /** Creates a search path from a string of pathnames. The path can be semicolon- or comma-separated, e.g. "/foo/bar;/foo/moose;/fish/moose" The separate folders are tokenised and added to the search path. */ FileSearchPath (const String& path); /** Creates a copy of another search path. */ FileSearchPath (const FileSearchPath&); /** Copies another search path. */ FileSearchPath& operator= (const FileSearchPath&); /** Destructor. */ ~FileSearchPath(); /** Uses a string containing a list of pathnames to re-initialise this list. This search path is cleared and the semicolon- or comma-separated folders in this string are added instead. e.g. "/foo/bar;/foo/moose;/fish/moose" */ FileSearchPath& operator= (const String& path); //============================================================================== /** Returns the number of folders in this search path. @see operator[] */ int getNumPaths() const; /** Returns one of the folders in this search path. The file returned isn't guaranteed to actually be a valid directory. @see getNumPaths */ File operator[] (int index) const; /** Returns the search path as a semicolon-separated list of directories. */ String toString() const; //============================================================================== /** Adds a new directory to the search path. The new directory is added to the end of the list if the insertIndex parameter is less than zero, otherwise it is inserted at the given index. */ void add (const File& directoryToAdd, int insertIndex = -1); /** Adds a new directory to the search path if it's not already in there. */ void addIfNotAlreadyThere (const File& directoryToAdd); /** Removes a directory from the search path. */ void remove (int indexToRemove); /** Merges another search path into this one. This will remove any duplicate directories. */ void addPath (const FileSearchPath&); /** Removes any directories that are actually subdirectories of one of the other directories in the search path. If the search is intended to be recursive, there's no point having nested folders in the search path, because they'll just get searched twice and you'll get duplicate results. e.g. if the path is "c:\abc\de;c:\abc", this method will simplify it to "c:\abc" */ void removeRedundantPaths(); /** Removes any directories that don't actually exist. */ void removeNonExistentPaths(); //============================================================================== /** Searches the path for a wildcard. This will search all the directories in the search path in order, adding any matching files to the results array. @param results an array to append the results to @param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying whether to return files, directories, or both. @param searchRecursively whether to recursively search the subdirectories too @param wildCardPattern a pattern to match against the filenames @returns the number of files added to the array @see File::findChildFiles */ int findChildFiles (Array& results, int whatToLookFor, bool searchRecursively, const String& wildCardPattern = "*") const; //============================================================================== /** Finds out whether a file is inside one of the path's directories. This will return true if the specified file is a child of one of the directories specified by this path. Note that this doesn't actually do any searching or check that the files exist - it just looks at the pathnames to work out whether the file would be inside a directory. @param fileToCheck the file to look for @param checkRecursively if true, then this will return true if the file is inside a subfolder of one of the path's directories (at any depth). If false it will only return true if the file is actually a direct child of one of the directories. @see File::isAChildOf */ bool isFileInPath (const File& fileToCheck, bool checkRecursively) const; private: //============================================================================== StringArray directories; void init (const String&); JUCE_LEAK_DETECTOR (FileSearchPath) }; #endif // JUCE_FILESEARCHPATH_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h000066400000000000000000000120131320201440200312170ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_MEMORYMAPPEDFILE_H_INCLUDED #define JUCE_MEMORYMAPPEDFILE_H_INCLUDED //============================================================================== /** Maps a file into virtual memory for easy reading and/or writing. */ class JUCE_API MemoryMappedFile { public: /** The read/write flags used when opening a memory mapped file. */ enum AccessMode { readOnly, /**< Indicates that the memory can only be read. */ readWrite /**< Indicates that the memory can be read and written to - changes that are made will be flushed back to disk at the whim of the OS. */ }; /** Opens a file and maps it to an area of virtual memory. The file should already exist, and should already be the size that you want to work with when you call this. If the file is resized after being opened, the behaviour is undefined. If the file exists and the operation succeeds, the getData() and getSize() methods will return the location and size of the data that can be read or written. Note that the entire file is not read into memory immediately - the OS simply creates a virtual mapping, which will lazily pull the data into memory when blocks are accessed. If the file can't be opened for some reason, the getData() method will return a null pointer. */ MemoryMappedFile (const File& file, AccessMode mode); /** Opens a section of a file and maps it to an area of virtual memory. The file should already exist, and should already be the size that you want to work with when you call this. If the file is resized after being opened, the behaviour is undefined. If the file exists and the operation succeeds, the getData() and getSize() methods will return the location and size of the data that can be read or written. Note that the entire file is not read into memory immediately - the OS simply creates a virtual mapping, which will lazily pull the data into memory when blocks are accessed. If the file can't be opened for some reason, the getData() method will return a null pointer. NOTE: the start of the actual range used may be rounded-down to a multiple of the OS's page-size, so do not assume that the mapped memory will begin at exactly the position you requested - always use getRange() to check the actual range that is being used. */ MemoryMappedFile (const File& file, const Range& fileRange, AccessMode mode); /** Destructor. */ ~MemoryMappedFile(); /** Returns the address at which this file has been mapped, or a null pointer if the file couldn't be successfully mapped. */ void* getData() const noexcept { return address; } /** Returns the number of bytes of data that are available for reading or writing. This will normally be the size of the file. */ size_t getSize() const noexcept { return (size_t) range.getLength(); } /** Returns the section of the file at which the mapped memory represents. */ Range getRange() const noexcept { return range; } private: //============================================================================== void* address; Range range; #if JUCE_WINDOWS void* fileHandle; #else int fileHandle; #endif void openInternal (const File&, AccessMode); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile) }; #endif // JUCE_MEMORYMAPPEDFILE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp000066400000000000000000000106471320201440200311500ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ static File createTempFile (const File& parentDirectory, String name, const String& suffix, const int optionFlags) { if ((optionFlags & TemporaryFile::useHiddenFile) != 0) name = "." + name; return parentDirectory.getNonexistentChildFile (name, suffix, (optionFlags & TemporaryFile::putNumbersInBrackets) != 0); } TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) : temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory), "temp_" + String::toHexString (Random::getSystemRandom().nextInt()), suffix, optionFlags)) { } TemporaryFile::TemporaryFile (const File& target, const int optionFlags) : temporaryFile (createTempFile (target.getParentDirectory(), target.getFileNameWithoutExtension() + "_temp" + String::toHexString (Random::getSystemRandom().nextInt()), target.getFileExtension(), optionFlags)), targetFile (target) { // If you use this constructor, you need to give it a valid target file! jassert (targetFile != File()); } TemporaryFile::TemporaryFile (const File& target, const File& temporary) : temporaryFile (temporary), targetFile (target) { } TemporaryFile::~TemporaryFile() { if (! deleteTemporaryFile()) { /* Failed to delete our temporary file! The most likely reason for this would be that you've not closed an output stream that was being used to write to file. If you find that something beyond your control is changing permissions on your temporary files and preventing them from being deleted, you may want to call TemporaryFile::deleteTemporaryFile() to detect those error cases and handle them appropriately. */ jassertfalse; } } //============================================================================== bool TemporaryFile::overwriteTargetFileWithTemporary() const { // This method only works if you created this object with the constructor // that takes a target file! jassert (targetFile != File()); if (temporaryFile.exists()) { // Have a few attempts at overwriting the file before giving up.. for (int i = 5; --i >= 0;) { if (temporaryFile.moveFileTo (targetFile)) return true; Thread::sleep (100); } } else { // There's no temporary file to use. If your write failed, you should // probably check, and not bother calling this method. jassertfalse; } return false; } bool TemporaryFile::deleteTemporaryFile() const { // Have a few attempts at deleting the file before giving up.. for (int i = 5; --i >= 0;) { if (temporaryFile.deleteFile()) return true; Thread::sleep (50); } return false; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h000066400000000000000000000164741320201440200306210ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_TEMPORARYFILE_H_INCLUDED #define JUCE_TEMPORARYFILE_H_INCLUDED //============================================================================== /** Manages a temporary file, which will be deleted when this object is deleted. This object is intended to be used as a stack based object, using its scope to make sure the temporary file isn't left lying around. For example: @code { File myTargetFile ("~/myfile.txt"); // this will choose a file called something like "~/myfile_temp239348.txt" // which definitely doesn't exist at the time the constructor is called. TemporaryFile temp (myTargetFile); // create a stream to the temporary file, and write some data to it... ScopedPointer out (temp.getFile().createOutputStream()); if (out != nullptr) { out->write ( ...etc ) out = nullptr; // (deletes the stream) // ..now we've finished writing, this will rename the temp file to // make it replace the target file we specified above. bool succeeded = temp.overwriteTargetFileWithTemporary(); } // ..and even if something went wrong and our overwrite failed, // as the TemporaryFile object goes out of scope here, it'll make sure // that the temp file gets deleted. } @endcode @see File, FileOutputStream */ class JUCE_API TemporaryFile { public: //============================================================================== enum OptionFlags { useHiddenFile = 1, /**< Indicates that the temporary file should be hidden - i.e. its name should start with a dot. */ putNumbersInBrackets = 2 /**< Indicates that when numbers are appended to make sure the file is unique, they should go in brackets rather than just being appended (see File::getNonexistentSibling() )*/ }; //============================================================================== /** Creates a randomly-named temporary file in the default temp directory. @param suffix a file suffix to use for the file @param optionFlags a combination of the values listed in the OptionFlags enum The file will not be created until you write to it. And remember that when this object is deleted, the file will also be deleted! */ TemporaryFile (const String& suffix = String(), int optionFlags = 0); /** Creates a temporary file in the same directory as a specified file. This is useful if you have a file that you want to overwrite, but don't want to harm the original file if the write operation fails. You can use this to create a temporary file next to the target file, then write to the temporary file, and finally use overwriteTargetFileWithTemporary() to replace the target file with the one you've just written. This class won't create any files until you actually write to them. And remember that when this object is deleted, the temporary file will also be deleted! @param targetFile the file that you intend to overwrite - the temporary file will be created in the same directory as this @param optionFlags a combination of the values listed in the OptionFlags enum */ TemporaryFile (const File& targetFile, int optionFlags = 0); /** Creates a temporary file using an explicit filename. The other constructors are a better choice than this one, unless for some reason you need to explicitly specify the temporary file you want to use. @param targetFile the file that you intend to overwrite @param temporaryFile the temporary file to be used */ TemporaryFile (const File& targetFile, const File& temporaryFile); /** Destructor. When this object is deleted it will make sure that its temporary file is also deleted! If the operation fails, it'll throw an assertion in debug mode. */ ~TemporaryFile(); //============================================================================== /** Returns the temporary file. */ const File& getFile() const noexcept { return temporaryFile; } /** Returns the target file that was specified in the constructor. */ const File& getTargetFile() const noexcept { return targetFile; } /** Tries to move the temporary file to overwrite the target file that was specified in the constructor. If you used the constructor that specified a target file, this will attempt to replace that file with the temporary one. Before calling this, make sure: - that you've actually written to the temporary file - that you've closed any open streams that you were using to write to it - and that you don't have any streams open to the target file, which would prevent it being overwritten If the file move succeeds, this returns false, and the temporary file will have disappeared. If it fails, the temporary file will probably still exist, but will be deleted when this object is destroyed. */ bool overwriteTargetFileWithTemporary() const; /** Attempts to delete the temporary file, if it exists. @returns true if the file is successfully deleted (or if it didn't exist). */ bool deleteTemporaryFile() const; private: //============================================================================== const File temporaryFile, targetFile; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryFile) }; #endif // JUCE_TEMPORARYFILE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp000066400000000000000000000055711320201440200320650ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ static void parseWildcard (const String& pattern, StringArray& result) { result.addTokens (pattern.toLowerCase(), ";,", "\"'"); result.trim(); result.removeEmptyStrings(); // special case for *.*, because people use it to mean "any file", but it // would actually ignore files with no extension. for (int i = result.size(); --i >= 0;) if (result[i] == "*.*") result.set (i, "*"); } static bool matchWildcard (const File& file, const StringArray& wildcards) { const String filename (file.getFileName()); for (int i = wildcards.size(); --i >= 0;) if (filename.matchesWildcard (wildcards[i], true)) return true; return false; } WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, const String& directoryWildcardPatterns, const String& desc) : FileFilter (desc.isEmpty() ? fileWildcardPatterns : (desc + " (" + fileWildcardPatterns + ")")) { parseWildcard (fileWildcardPatterns, fileWildcards); parseWildcard (directoryWildcardPatterns, directoryWildcards); } WildcardFileFilter::~WildcardFileFilter() { } bool WildcardFileFilter::isFileSuitable (const File& file) const { return matchWildcard (file, fileWildcards); } bool WildcardFileFilter::isDirectorySuitable (const File& file) const { return matchWildcard (file, directoryWildcards); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h000066400000000000000000000066221320201440200315300ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_WILDCARDFILEFILTER_H_INCLUDED #define JUCE_WILDCARDFILEFILTER_H_INCLUDED //============================================================================== /** A type of FileFilter that works by wildcard pattern matching. This filter only allows files that match one of the specified patterns, but allows all directories through. @see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent */ class JUCE_API WildcardFileFilter : public FileFilter { public: //============================================================================== /** Creates a wildcard filter for one or more patterns. The wildcardPatterns parameter is a comma or semicolon-delimited set of patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav or .aiff. Passing an empty string as a pattern will fail to match anything, so by leaving either the file or directory pattern parameter empty means you can control whether files or directories are found. The description is a name to show the user in a list of possible patterns, so for the wav/aiff example, your description might be "audio files". */ WildcardFileFilter (const String& fileWildcardPatterns, const String& directoryWildcardPatterns, const String& description); /** Destructor. */ ~WildcardFileFilter(); //============================================================================== /** Returns true if the filename matches one of the patterns specified. */ bool isFileSuitable (const File& file) const; /** This always returns true. */ bool isDirectorySuitable (const File& file) const; private: //============================================================================== StringArray fileWildcards, directoryWildcards; JUCE_LEAK_DETECTOR (WildcardFileFilter) }; #endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/javascript/000077500000000000000000000000001320201440200257105ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp000066400000000000000000000463401320201440200302020ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ class JSONParser { public: static Result parseObjectOrArray (String::CharPointerType t, var& result) { t = t.findEndOfWhitespace(); switch (t.getAndAdvance()) { case 0: result = var(); return Result::ok(); case '{': return parseObject (t, result); case '[': return parseArray (t, result); } return createFail ("Expected '{' or '['", &t); } static Result parseString (const juce_wchar quoteChar, String::CharPointerType& t, var& result) { MemoryOutputStream buffer (256); for (;;) { juce_wchar c = t.getAndAdvance(); if (c == quoteChar) break; if (c == '\\') { c = t.getAndAdvance(); switch (c) { case '"': case '\'': case '\\': case '/': break; case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'u': { c = 0; for (int i = 4; --i >= 0;) { const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (digitValue < 0) return createFail ("Syntax error in unicode escape sequence"); c = (juce_wchar) ((c << 4) + digitValue); } break; } } } if (c == 0) return createFail ("Unexpected end-of-input in string constant"); buffer.appendUTF8Char (c); } result = buffer.toUTF8(); return Result::ok(); } static Result parseAny (String::CharPointerType& t, var& result) { t = t.findEndOfWhitespace(); String::CharPointerType t2 (t); switch (t2.getAndAdvance()) { case '{': t = t2; return parseObject (t, result); case '[': t = t2; return parseArray (t, result); case '"': t = t2; return parseString ('"', t, result); case '\'': t = t2; return parseString ('\'', t, result); case '-': t2 = t2.findEndOfWhitespace(); if (! CharacterFunctions::isDigit (*t2)) break; t = t2; return parseNumber (t, result, true); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return parseNumber (t, result, false); case 't': // "true" if (t2.getAndAdvance() == 'r' && t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'e') { t = t2; result = var (true); return Result::ok(); } break; case 'f': // "false" if (t2.getAndAdvance() == 'a' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 's' && t2.getAndAdvance() == 'e') { t = t2; result = var (false); return Result::ok(); } break; case 'n': // "null" if (t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 'l') { t = t2; result = var(); return Result::ok(); } break; default: break; } return createFail ("Syntax error", &t); } private: static Result createFail (const char* const message, const String::CharPointerType* location = nullptr) { String m (message); if (location != nullptr) m << ": \"" << String (*location, 20) << '"'; return Result::fail (m); } static Result parseNumber (String::CharPointerType& t, var& result, const bool isNegative) { String::CharPointerType oldT (t); int64 intValue = t.getAndAdvance() - '0'; jassert (intValue >= 0 && intValue < 10); for (;;) { String::CharPointerType previousChar (t); const juce_wchar c = t.getAndAdvance(); const int digit = ((int) c) - '0'; if (isPositiveAndBelow (digit, 10)) { intValue = intValue * 10 + digit; continue; } if (c == 'e' || c == 'E' || c == '.') { t = oldT; const double asDouble = CharacterFunctions::readDoubleValue (t); result = isNegative ? -asDouble : asDouble; return Result::ok(); } if (CharacterFunctions::isWhitespace (c) || c == ',' || c == '}' || c == ']' || c == 0) { t = previousChar; break; } return createFail ("Syntax error in number", &oldT); } const int64 correctedValue = isNegative ? -intValue : intValue; if ((intValue >> 31) != 0) result = correctedValue; else result = (int) correctedValue; return Result::ok(); } static Result parseObject (String::CharPointerType& t, var& result) { DynamicObject* const resultObject = new DynamicObject(); result = resultObject; NamedValueSet& resultProperties = resultObject->getProperties(); for (;;) { t = t.findEndOfWhitespace(); String::CharPointerType oldT (t); const juce_wchar c = t.getAndAdvance(); if (c == '}') break; if (c == 0) return createFail ("Unexpected end-of-input in object declaration"); if (c == '"') { var propertyNameVar; Result r (parseString ('"', t, propertyNameVar)); if (r.failed()) return r; const Identifier propertyName (propertyNameVar.toString()); if (propertyName.isValid()) { t = t.findEndOfWhitespace(); oldT = t; const juce_wchar c2 = t.getAndAdvance(); if (c2 != ':') return createFail ("Expected ':', but found", &oldT); resultProperties.set (propertyName, var()); var* propertyValue = resultProperties.getVarPointer (propertyName); Result r2 (parseAny (t, *propertyValue)); if (r2.failed()) return r2; t = t.findEndOfWhitespace(); oldT = t; const juce_wchar nextChar = t.getAndAdvance(); if (nextChar == ',') continue; if (nextChar == '}') break; } } return createFail ("Expected object member declaration, but found", &oldT); } return Result::ok(); } static Result parseArray (String::CharPointerType& t, var& result) { result = var (Array()); Array* const destArray = result.getArray(); for (;;) { t = t.findEndOfWhitespace(); String::CharPointerType oldT (t); const juce_wchar c = t.getAndAdvance(); if (c == ']') break; if (c == 0) return createFail ("Unexpected end-of-input in array declaration"); t = oldT; destArray->add (var()); Result r (parseAny (t, destArray->getReference (destArray->size() - 1))); if (r.failed()) return r; t = t.findEndOfWhitespace(); oldT = t; const juce_wchar nextChar = t.getAndAdvance(); if (nextChar == ',') continue; if (nextChar == ']') break; return createFail ("Expected object array item, but found", &oldT); } return Result::ok(); } }; //============================================================================== class JSONFormatter { public: static void write (OutputStream& out, const var& v, const int indentLevel, const bool allOnOneLine) { if (v.isString()) { out << '"'; writeString (out, v.toString().getCharPointer()); out << '"'; } else if (v.isVoid()) { out << "null"; } else if (v.isUndefined()) { out << "undefined"; } else if (v.isBool()) { out << (static_cast (v) ? "true" : "false"); } else if (v.isArray()) { writeArray (out, *v.getArray(), indentLevel, allOnOneLine); } else if (v.isObject()) { if (DynamicObject* object = v.getDynamicObject()) object->writeAsJSON (out, indentLevel, allOnOneLine); else jassertfalse; // Only DynamicObjects can be converted to JSON! } else { // Can't convert these other types of object to JSON! jassert (! (v.isMethod() || v.isBinaryData())); out << v.toString(); } } static void writeEscapedChar (OutputStream& out, const unsigned short value) { out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4); } static void writeString (OutputStream& out, String::CharPointerType t) { for (;;) { const juce_wchar c (t.getAndAdvance()); switch (c) { case 0: return; case '\"': out << "\\\""; break; case '\\': out << "\\\\"; break; case '\a': out << "\\a"; break; case '\b': out << "\\b"; break; case '\f': out << "\\f"; break; case '\t': out << "\\t"; break; case '\r': out << "\\r"; break; case '\n': out << "\\n"; break; default: if (c >= 32 && c < 127) { out << (char) c; } else { if (CharPointer_UTF16::getBytesRequiredFor (c) > 2) { CharPointer_UTF16::CharType chars[2]; CharPointer_UTF16 utf16 (chars); utf16.write (c); for (int i = 0; i < 2; ++i) writeEscapedChar (out, (unsigned short) chars[i]); } else { writeEscapedChar (out, (unsigned short) c); } } break; } } } static void writeSpaces (OutputStream& out, int numSpaces) { out.writeRepeatedByte (' ', (size_t) numSpaces); } static void writeArray (OutputStream& out, const Array& array, const int indentLevel, const bool allOnOneLine) { out << '['; if (array.size() > 0) { if (! allOnOneLine) out << newLine; for (int i = 0; i < array.size(); ++i) { if (! allOnOneLine) writeSpaces (out, indentLevel + indentSize); write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine); if (i < array.size() - 1) { if (allOnOneLine) out << ", "; else out << ',' << newLine; } else if (! allOnOneLine) out << newLine; } if (! allOnOneLine) writeSpaces (out, indentLevel); } out << ']'; } enum { indentSize = 2 }; }; //============================================================================== var JSON::parse (const String& text) { var result; if (! parse (text, result)) result = var(); return result; } var JSON::fromString (StringRef text) { var result; if (! JSONParser::parseAny (text.text, result)) result = var(); return result; } var JSON::parse (InputStream& input) { return parse (input.readEntireStreamAsString()); } var JSON::parse (const File& file) { return parse (file.loadFileAsString()); } Result JSON::parse (const String& text, var& result) { return JSONParser::parseObjectOrArray (text.getCharPointer(), result); } String JSON::toString (const var& data, const bool allOnOneLine) { MemoryOutputStream mo (1024); JSONFormatter::write (mo, data, 0, allOnOneLine); return mo.toUTF8(); } void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine) { JSONFormatter::write (output, data, 0, allOnOneLine); } String JSON::escapeString (StringRef s) { MemoryOutputStream mo; JSONFormatter::writeString (mo, s.text); return mo.toString(); } Result JSON::parseQuotedString (String::CharPointerType& t, var& result) { const juce_wchar quote = t.getAndAdvance(); if (quote == '"' || quote == '\'') return JSONParser::parseString (quote, t, result); return Result::fail ("Not a quoted string!"); } //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class JSONTests : public UnitTest { public: JSONTests() : UnitTest ("JSON") {} static String createRandomWideCharString (Random& r) { juce_wchar buffer[40] = { 0 }; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { if (r.nextBool()) { do { buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); } while (! CharPointer_UTF16::canRepresent (buffer[i])); } else buffer[i] = (juce_wchar) (1 + r.nextInt (0xff)); } return CharPointer_UTF32 (buffer); } static String createRandomIdentifier (Random& r) { char buffer[30] = { 0 }; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { static const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:"; buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; } return CharPointer_ASCII (buffer); } static var createRandomVar (Random& r, int depth) { switch (r.nextInt (depth > 3 ? 6 : 8)) { case 0: return var(); case 1: return r.nextInt(); case 2: return r.nextInt64(); case 3: return r.nextBool(); case 4: return String (r.nextDouble(), 8).getDoubleValue(); case 5: return createRandomWideCharString (r); case 6: { var v (createRandomVar (r, depth + 1)); for (int i = 1 + r.nextInt (30); --i >= 0;) v.append (createRandomVar (r, depth + 1)); return v; } case 7: { DynamicObject* o = new DynamicObject(); for (int i = r.nextInt (30); --i >= 0;) o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1)); return o; } default: return var(); } } void runTest() { beginTest ("JSON"); Random r = getRandom(); expect (JSON::parse (String::empty) == var::null); expect (JSON::parse ("{}").isObject()); expect (JSON::parse ("[]").isArray()); expect (JSON::parse ("[ 1234 ]")[0].isInt()); expect (JSON::parse ("[ 12345678901234 ]")[0].isInt64()); expect (JSON::parse ("[ 1.123e3 ]")[0].isDouble()); expect (JSON::parse ("[ -1234]")[0].isInt()); expect (JSON::parse ("[-12345678901234]")[0].isInt64()); expect (JSON::parse ("[-1.123e3]")[0].isDouble()); for (int i = 100; --i >= 0;) { var v; if (i > 0) v = createRandomVar (r, 0); const bool oneLine = r.nextBool(); String asString (JSON::toString (v, oneLine)); var parsed = JSON::parse ("[" + asString + "]")[0]; String parsedString (JSON::toString (parsed, oneLine)); expect (asString.isNotEmpty() && parsedString == asString); } } }; static JSONTests JSONUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h000066400000000000000000000144351320201440200276470ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_JSON_H_INCLUDED #define JUCE_JSON_H_INCLUDED //============================================================================== /** Contains static methods for converting JSON-formatted text to and from var objects. The var class is structurally compatible with JSON-formatted data, so these functions allow you to parse JSON into a var object, and to convert a var object to JSON-formatted text. @see var */ class JUCE_API JSON { public: //============================================================================== /** Parses a string of JSON-formatted text, and returns a result code containing any parse errors. This will return the parsed structure in the parsedResult parameter, and will return a Result object to indicate whether parsing was successful, and if not, it will contain an error message. If you're not interested in the error message, you can use one of the other shortcut parse methods, which simply return a var::null if the parsing fails. Note that this will only parse valid JSON, which means that the item given must be either an object or an array definition. If you want to also be able to parse any kind of primitive JSON object, use the fromString() method. */ static Result parse (const String& text, var& parsedResult); /** Attempts to parse some JSON-formatted text, and returns the result as a var object. If the parsing fails, this simply returns var::null - if you need to find out more detail about the parse error, use the alternative parse() method which returns a Result. Note that this will only parse valid JSON, which means that the item given must be either an object or an array definition. If you want to also be able to parse any kind of primitive JSON object, use the fromString() method. */ static var parse (const String& text); /** Attempts to parse some JSON-formatted text from a file, and returns the result as a var object. Note that this is just a short-cut for reading the entire file into a string and parsing the result. If the parsing fails, this simply returns var::null - if you need to find out more detail about the parse error, use the alternative parse() method which returns a Result. */ static var parse (const File& file); /** Attempts to parse some JSON-formatted text from a stream, and returns the result as a var object. Note that this is just a short-cut for reading the entire stream into a string and parsing the result. If the parsing fails, this simply returns var::null - if you need to find out more detail about the parse error, use the alternative parse() method which returns a Result. */ static var parse (InputStream& input); //============================================================================== /** Returns a string which contains a JSON-formatted representation of the var object. If allOnOneLine is true, the result will be compacted into a single line of text with no carriage-returns. If false, it will be laid-out in a more human-readable format. @see writeToStream */ static String toString (const var& objectToFormat, bool allOnOneLine = false); /** Parses a string that was created with the toString() method. This is slightly different to the parse() methods because they will reject primitive values and only accept array or object definitions, whereas this method will handle either. */ static var fromString (StringRef); /** Writes a JSON-formatted representation of the var object to the given stream. If allOnOneLine is true, the result will be compacted into a single line of text with no carriage-returns. If false, it will be laid-out in a more human-readable format. @see toString */ static void writeToStream (OutputStream& output, const var& objectToFormat, bool allOnOneLine = false); /** Returns a version of a string with any extended characters escaped. */ static String escapeString (StringRef); /** Parses a quoted string-literal in JSON format, returning the un-escaped result in the result parameter, and an error message in case the content was illegal. This advances the text parameter, leaving it positioned after the closing quote. */ static Result parseQuotedString (String::CharPointerType& text, var& result); private: //============================================================================== JSON() JUCE_DELETED_FUNCTION; // This class can't be instantiated - just use its static methods. }; #endif // JUCE_JSON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp000066400000000000000000002176071320201440200315450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #define JUCE_JS_OPERATORS(X) \ X(semicolon, ";") X(dot, ".") X(comma, ",") \ X(openParen, "(") X(closeParen, ")") X(openBrace, "{") X(closeBrace, "}") \ X(openBracket, "[") X(closeBracket, "]") X(colon, ":") X(question, "?") \ X(typeEquals, "===") X(equals, "==") X(assign, "=") \ X(typeNotEquals, "!==") X(notEquals, "!=") X(logicalNot, "!") \ X(plusEquals, "+=") X(plusplus, "++") X(plus, "+") \ X(minusEquals, "-=") X(minusminus, "--") X(minus, "-") \ X(timesEquals, "*=") X(times, "*") X(divideEquals, "/=") X(divide, "/") \ X(moduloEquals, "%=") X(modulo, "%") X(xorEquals, "^=") X(bitwiseXor, "^") \ X(andEquals, "&=") X(logicalAnd, "&&") X(bitwiseAnd, "&") \ X(orEquals, "|=") X(logicalOr, "||") X(bitwiseOr, "|") \ X(leftShiftEquals, "<<=") X(lessThanOrEqual, "<=") X(leftShift, "<<") X(lessThan, "<") \ X(rightShiftUnsigned, ">>>") X(rightShiftEquals, ">>=") X(rightShift, ">>") X(greaterThanOrEqual, ">=") X(greaterThan, ">") #define JUCE_JS_KEYWORDS(X) \ X(var, "var") X(if_, "if") X(else_, "else") X(do_, "do") X(null_, "null") \ X(while_, "while") X(for_, "for") X(break_, "break") X(continue_, "continue") X(undefined, "undefined") \ X(function, "function") X(return_, "return") X(true_, "true") X(false_, "false") X(new_, "new") namespace TokenTypes { #define JUCE_DECLARE_JS_TOKEN(name, str) static const char* const name = str; JUCE_JS_KEYWORDS (JUCE_DECLARE_JS_TOKEN) JUCE_JS_OPERATORS (JUCE_DECLARE_JS_TOKEN) JUCE_DECLARE_JS_TOKEN (eof, "$eof") JUCE_DECLARE_JS_TOKEN (literal, "$literal") JUCE_DECLARE_JS_TOKEN (identifier, "$identifier") } #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4702) #endif //============================================================================== struct JavascriptEngine::RootObject : public DynamicObject { RootObject() { setMethod ("exec", exec); setMethod ("eval", eval); setMethod ("trace", trace); setMethod ("charToInt", charToInt); setMethod ("parseInt", IntegerClass::parseInt); } Time timeout; typedef const var::NativeFunctionArgs& Args; typedef const char* TokenType; void execute (const String& code) { ExpressionTreeBuilder tb (code); ScopedPointer (tb.parseStatementList())->perform (Scope (nullptr, this, this), nullptr); } var evaluate (const String& code) { ExpressionTreeBuilder tb (code); return ExpPtr (tb.parseExpression())->getResult (Scope (nullptr, this, this)); } //============================================================================== static bool areTypeEqual (const var& a, const var& b) { return a.hasSameTypeAs (b) && isFunction (a) == isFunction (b) && (((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) || a == b); } static String getTokenName (TokenType t) { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); } static bool isFunction (const var& v) { return dynamic_cast (v.getObject()) != nullptr; } static bool isNumericOrUndefined (const var& v) { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool() || v.isUndefined(); } static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); } static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; } static var* getPropertyPointer (DynamicObject* o, const Identifier& i) { return o->getProperties().getVarPointer (i); } //============================================================================== struct CodeLocation { CodeLocation (const String& code) noexcept : program (code), location (program.getCharPointer()) {} CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {} void throwError (const String& message) const { int col = 1, line = 1; for (String::CharPointerType i (program.getCharPointer()); i < location && ! i.isEmpty(); ++i) { ++col; if (*i == '\n') { col = 1; ++line; } } throw "Line " + String (line) + ", column " + String (col) + " : " + message; } String program; String::CharPointerType location; }; //============================================================================== struct Scope { Scope (const Scope* p, RootObject* r, DynamicObject* s) noexcept : parent (p), root (r), scope (s) {} const Scope* parent; ReferenceCountedObjectPtr root; DynamicObject::Ptr scope; var findFunctionCall (const CodeLocation& location, const var& targetObject, const Identifier& functionName) const { if (DynamicObject* o = targetObject.getDynamicObject()) { if (const var* prop = getPropertyPointer (o, functionName)) return *prop; for (DynamicObject* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr; p = p->getProperty (getPrototypeIdentifier()).getDynamicObject()) { if (const var* prop = getPropertyPointer (p, functionName)) return *prop; } // if there's a class with an overridden DynamicObject::hasMethod, this avoids an error if (o->hasMethod (functionName)) return var(); } if (targetObject.isString()) if (var* m = findRootClassProperty (StringClass::getClassName(), functionName)) return *m; if (targetObject.isArray()) if (var* m = findRootClassProperty (ArrayClass::getClassName(), functionName)) return *m; if (var* m = findRootClassProperty (ObjectClass::getClassName(), functionName)) return *m; location.throwError ("Unknown function '" + functionName.toString() + "'"); return var(); } var* findRootClassProperty (const Identifier& className, const Identifier& propName) const { if (DynamicObject* cls = root->getProperty (className).getDynamicObject()) return getPropertyPointer (cls, propName); return nullptr; } var findSymbolInParentScopes (const Identifier& name) const { if (const var* v = getPropertyPointer (scope, name)) return *v; return parent != nullptr ? parent->findSymbolInParentScopes (name) : var::undefined(); } bool findAndInvokeMethod (const Identifier& function, const var::NativeFunctionArgs& args, var& result) const { DynamicObject* target = args.thisObject.getDynamicObject(); if (target == nullptr || target == scope) { if (const var* m = getPropertyPointer (scope, function)) { if (FunctionObject* fo = dynamic_cast (m->getObject())) { result = fo->invoke (*this, args); return true; } } } const NamedValueSet& props = scope->getProperties(); for (int i = 0; i < props.size(); ++i) if (DynamicObject* o = props.getValueAt (i).getDynamicObject()) if (Scope (this, root, o).findAndInvokeMethod (function, args, result)) return true; return false; } void checkTimeOut (const CodeLocation& location) const { if (Time::getCurrentTime() > root->timeout) location.throwError ("Execution timed-out"); } }; //============================================================================== struct Statement { Statement (const CodeLocation& l) noexcept : location (l) {} virtual ~Statement() {} enum ResultCode { ok = 0, returnWasHit, breakWasHit, continueWasHit }; virtual ResultCode perform (const Scope&, var*) const { return ok; } CodeLocation location; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Statement) }; struct Expression : public Statement { Expression (const CodeLocation& l) noexcept : Statement (l) {} virtual var getResult (const Scope&) const { return var::undefined(); } virtual void assign (const Scope&, const var&) const { location.throwError ("Cannot assign to this expression!"); } ResultCode perform (const Scope& s, var*) const override { getResult (s); return ok; } }; typedef ScopedPointer ExpPtr; struct BlockStatement : public Statement { BlockStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope& s, var* returnedValue) const override { for (int i = 0; i < statements.size(); ++i) if (ResultCode r = statements.getUnchecked(i)->perform (s, returnedValue)) return r; return ok; } OwnedArray statements; }; struct IfStatement : public Statement { IfStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope& s, var* returnedValue) const override { return (condition->getResult(s) ? trueBranch : falseBranch)->perform (s, returnedValue); } ExpPtr condition; ScopedPointer trueBranch, falseBranch; }; struct VarStatement : public Statement { VarStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope& s, var*) const override { s.scope->setProperty (name, initialiser->getResult (s)); return ok; } Identifier name; ExpPtr initialiser; }; struct LoopStatement : public Statement { LoopStatement (const CodeLocation& l, bool isDo) noexcept : Statement (l), isDoLoop (isDo) {} ResultCode perform (const Scope& s, var* returnedValue) const override { initialiser->perform (s, nullptr); while (isDoLoop || condition->getResult (s)) { s.checkTimeOut (location); ResultCode r = body->perform (s, returnedValue); if (r == returnWasHit) return r; if (r == breakWasHit) break; iterator->perform (s, nullptr); if (isDoLoop && r != continueWasHit && ! condition->getResult (s)) break; } return ok; } ScopedPointer initialiser, iterator, body; ExpPtr condition; bool isDoLoop; }; struct ReturnStatement : public Statement { ReturnStatement (const CodeLocation& l, Expression* v) noexcept : Statement (l), returnValue (v) {} ResultCode perform (const Scope& s, var* ret) const override { if (ret != nullptr) *ret = returnValue->getResult (s); return returnWasHit; } ExpPtr returnValue; }; struct BreakStatement : public Statement { BreakStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope&, var*) const override { return breakWasHit; } }; struct ContinueStatement : public Statement { ContinueStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope&, var*) const override { return continueWasHit; } }; struct LiteralValue : public Expression { LiteralValue (const CodeLocation& l, const var& v) noexcept : Expression (l), value (v) {} var getResult (const Scope&) const override { return value; } var value; }; struct UnqualifiedName : public Expression { UnqualifiedName (const CodeLocation& l, const Identifier& n) noexcept : Expression (l), name (n) {} var getResult (const Scope& s) const override { return s.findSymbolInParentScopes (name); } void assign (const Scope& s, const var& newValue) const override { if (var* v = getPropertyPointer (s.scope, name)) *v = newValue; else s.root->setProperty (name, newValue); } Identifier name; }; struct DotOperator : public Expression { DotOperator (const CodeLocation& l, ExpPtr& p, const Identifier& c) noexcept : Expression (l), parent (p), child (c) {} var getResult (const Scope& s) const override { var p (parent->getResult (s)); static const Identifier lengthID ("length"); if (child == lengthID) { if (Array* array = p.getArray()) return array->size(); if (p.isString()) return p.toString().length(); } if (DynamicObject* o = p.getDynamicObject()) if (const var* v = getPropertyPointer (o, child)) return *v; return var::undefined(); } void assign (const Scope& s, const var& newValue) const override { if (DynamicObject* o = parent->getResult (s).getDynamicObject()) o->setProperty (child, newValue); else Expression::assign (s, newValue); } ExpPtr parent; Identifier child; }; struct ArraySubscript : public Expression { ArraySubscript (const CodeLocation& l) noexcept : Expression (l) {} var getResult (const Scope& s) const override { if (const Array* array = object->getResult (s).getArray()) return (*array) [static_cast (index->getResult (s))]; return var::undefined(); } void assign (const Scope& s, const var& newValue) const override { if (Array* array = object->getResult (s).getArray()) { const int i = index->getResult (s); while (array->size() < i) array->add (var::undefined()); array->set (i, newValue); return; } Expression::assign (s, newValue); } ExpPtr object, index; }; struct BinaryOperatorBase : public Expression { BinaryOperatorBase (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept : Expression (l), lhs (a), rhs (b), operation (op) {} ExpPtr lhs, rhs; TokenType operation; }; struct BinaryOperator : public BinaryOperatorBase { BinaryOperator (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept : BinaryOperatorBase (l, a, b, op) {} virtual var getWithUndefinedArg() const { return var::undefined(); } virtual var getWithDoubles (double, double) const { return throwError ("Double"); } virtual var getWithInts (int64, int64) const { return throwError ("Integer"); } virtual var getWithArrayOrObject (const var& a, const var&) const { return throwError (a.isArray() ? "Array" : "Object"); } virtual var getWithStrings (const String&, const String&) const { return throwError ("String"); } var getResult (const Scope& s) const override { var a (lhs->getResult (s)), b (rhs->getResult (s)); if ((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) return getWithUndefinedArg(); if (isNumericOrUndefined (a) && isNumericOrUndefined (b)) return (a.isDouble() || b.isDouble()) ? getWithDoubles (a, b) : getWithInts (a, b); if (a.isArray() || a.isObject()) return getWithArrayOrObject (a, b); return getWithStrings (a.toString(), b.toString()); } var throwError (const char* typeName) const { location.throwError (getTokenName (operation) + " is not allowed on the " + typeName + " type"); return var(); } }; struct EqualsOp : public BinaryOperator { EqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::equals) {} var getWithUndefinedArg() const override { return true; } var getWithDoubles (double a, double b) const override { return a == b; } var getWithInts (int64 a, int64 b) const override { return a == b; } var getWithStrings (const String& a, const String& b) const override { return a == b; } var getWithArrayOrObject (const var& a, const var& b) const override { return a == b; } }; struct NotEqualsOp : public BinaryOperator { NotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::notEquals) {} var getWithUndefinedArg() const override { return false; } var getWithDoubles (double a, double b) const override { return a != b; } var getWithInts (int64 a, int64 b) const override { return a != b; } var getWithStrings (const String& a, const String& b) const override { return a != b; } var getWithArrayOrObject (const var& a, const var& b) const override { return a != b; } }; struct LessThanOp : public BinaryOperator { LessThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThan) {} var getWithDoubles (double a, double b) const override { return a < b; } var getWithInts (int64 a, int64 b) const override { return a < b; } var getWithStrings (const String& a, const String& b) const override { return a < b; } }; struct LessThanOrEqualOp : public BinaryOperator { LessThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThanOrEqual) {} var getWithDoubles (double a, double b) const override { return a <= b; } var getWithInts (int64 a, int64 b) const override { return a <= b; } var getWithStrings (const String& a, const String& b) const override { return a <= b; } }; struct GreaterThanOp : public BinaryOperator { GreaterThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThan) {} var getWithDoubles (double a, double b) const override { return a > b; } var getWithInts (int64 a, int64 b) const override { return a > b; } var getWithStrings (const String& a, const String& b) const override { return a > b; } }; struct GreaterThanOrEqualOp : public BinaryOperator { GreaterThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThanOrEqual) {} var getWithDoubles (double a, double b) const override { return a >= b; } var getWithInts (int64 a, int64 b) const override { return a >= b; } var getWithStrings (const String& a, const String& b) const override { return a >= b; } }; struct AdditionOp : public BinaryOperator { AdditionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::plus) {} var getWithDoubles (double a, double b) const override { return a + b; } var getWithInts (int64 a, int64 b) const override { return a + b; } var getWithStrings (const String& a, const String& b) const override { return a + b; } }; struct SubtractionOp : public BinaryOperator { SubtractionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::minus) {} var getWithDoubles (double a, double b) const override { return a - b; } var getWithInts (int64 a, int64 b) const override { return a - b; } }; struct MultiplyOp : public BinaryOperator { MultiplyOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::times) {} var getWithDoubles (double a, double b) const override { return a * b; } var getWithInts (int64 a, int64 b) const override { return a * b; } }; struct DivideOp : public BinaryOperator { DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {} var getWithDoubles (double a, double b) const override { return b != 0 ? a / b : std::numeric_limits::infinity(); } var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a / (double) b) : var (std::numeric_limits::infinity()); } }; struct ModuloOp : public BinaryOperator { ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {} var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a % b) : var (std::numeric_limits::infinity()); } }; struct BitwiseOrOp : public BinaryOperator { BitwiseOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseOr) {} var getWithInts (int64 a, int64 b) const override { return a | b; } }; struct BitwiseAndOp : public BinaryOperator { BitwiseAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseAnd) {} var getWithInts (int64 a, int64 b) const override { return a & b; } }; struct BitwiseXorOp : public BinaryOperator { BitwiseXorOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseXor) {} var getWithInts (int64 a, int64 b) const override { return a ^ b; } }; struct LeftShiftOp : public BinaryOperator { LeftShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::leftShift) {} var getWithInts (int64 a, int64 b) const override { return ((int) a) << (int) b; } }; struct RightShiftOp : public BinaryOperator { RightShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShift) {} var getWithInts (int64 a, int64 b) const override { return ((int) a) >> (int) b; } }; struct RightShiftUnsignedOp : public BinaryOperator { RightShiftUnsignedOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShiftUnsigned) {} var getWithInts (int64 a, int64 b) const override { return (int) (((uint32) a) >> (int) b); } }; struct LogicalAndOp : public BinaryOperatorBase { LogicalAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalAnd) {} var getResult (const Scope& s) const override { return lhs->getResult (s) && rhs->getResult (s); } }; struct LogicalOrOp : public BinaryOperatorBase { LogicalOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalOr) {} var getResult (const Scope& s) const override { return lhs->getResult (s) || rhs->getResult (s); } }; struct TypeEqualsOp : public BinaryOperatorBase { TypeEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeEquals) {} var getResult (const Scope& s) const override { return areTypeEqual (lhs->getResult (s), rhs->getResult (s)); } }; struct TypeNotEqualsOp : public BinaryOperatorBase { TypeNotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeNotEquals) {} var getResult (const Scope& s) const override { return ! areTypeEqual (lhs->getResult (s), rhs->getResult (s)); } }; struct ConditionalOp : public Expression { ConditionalOp (const CodeLocation& l) noexcept : Expression (l) {} var getResult (const Scope& s) const override { return (condition->getResult (s) ? trueBranch : falseBranch)->getResult (s); } void assign (const Scope& s, const var& v) const override { (condition->getResult (s) ? trueBranch : falseBranch)->assign (s, v); } ExpPtr condition, trueBranch, falseBranch; }; struct Assignment : public Expression { Assignment (const CodeLocation& l, ExpPtr& dest, ExpPtr& source) noexcept : Expression (l), target (dest), newValue (source) {} var getResult (const Scope& s) const override { var value (newValue->getResult (s)); target->assign (s, value); return value; } ExpPtr target, newValue; }; struct SelfAssignment : public Expression { SelfAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept : Expression (l), target (dest), newValue (source) {} var getResult (const Scope& s) const override { var value (newValue->getResult (s)); target->assign (s, value); return value; } Expression* target; // Careful! this pointer aliases a sub-term of newValue! ExpPtr newValue; TokenType op; }; struct PostAssignment : public SelfAssignment { PostAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept : SelfAssignment (l, dest, source) {} var getResult (const Scope& s) const override { var oldValue (target->getResult (s)); target->assign (s, newValue->getResult (s)); return oldValue; } }; struct FunctionCall : public Expression { FunctionCall (const CodeLocation& l) noexcept : Expression (l) {} var getResult (const Scope& s) const override { if (DotOperator* dot = dynamic_cast (object.get())) { var thisObject (dot->parent->getResult (s)); return invokeFunction (s, s.findFunctionCall (location, thisObject, dot->child), thisObject); } var function (object->getResult (s)); return invokeFunction (s, function, var (s.scope)); } var invokeFunction (const Scope& s, const var& function, const var& thisObject) const { s.checkTimeOut (location); Array argVars; for (int i = 0; i < arguments.size(); ++i) argVars.add (arguments.getUnchecked(i)->getResult (s)); const var::NativeFunctionArgs args (thisObject, argVars.begin(), argVars.size()); if (var::NativeFunction nativeFunction = function.getNativeFunction()) return nativeFunction (args); if (FunctionObject* fo = dynamic_cast (function.getObject())) return fo->invoke (s, args); if (DotOperator* dot = dynamic_cast (object.get())) if (DynamicObject* o = thisObject.getDynamicObject()) if (o->hasMethod (dot->child)) // allow an overridden DynamicObject::invokeMethod to accept a method call. return o->invokeMethod (dot->child, args); location.throwError ("This expression is not a function!"); return var(); } ExpPtr object; OwnedArray arguments; }; struct NewOperator : public FunctionCall { NewOperator (const CodeLocation& l) noexcept : FunctionCall (l) {} var getResult (const Scope& s) const override { var classOrFunc = object->getResult (s); const bool isFunc = isFunction (classOrFunc); if (! (isFunc || classOrFunc.getDynamicObject() != nullptr)) return var::undefined(); DynamicObject::Ptr newObject (new DynamicObject()); if (isFunc) invokeFunction (s, classOrFunc, newObject.get()); else newObject->setProperty (getPrototypeIdentifier(), classOrFunc); return newObject.get(); } }; struct ObjectDeclaration : public Expression { ObjectDeclaration (const CodeLocation& l) noexcept : Expression (l) {} var getResult (const Scope& s) const override { DynamicObject::Ptr newObject (new DynamicObject()); for (int i = 0; i < names.size(); ++i) newObject->setProperty (names.getUnchecked(i), initialisers.getUnchecked(i)->getResult (s)); return newObject.get(); } Array names; OwnedArray initialisers; }; struct ArrayDeclaration : public Expression { ArrayDeclaration (const CodeLocation& l) noexcept : Expression (l) {} var getResult (const Scope& s) const override { Array a; for (int i = 0; i < values.size(); ++i) a.add (values.getUnchecked(i)->getResult (s)); return a; } OwnedArray values; }; //============================================================================== struct FunctionObject : public DynamicObject { FunctionObject() noexcept {} FunctionObject (const FunctionObject& other) : DynamicObject(), functionCode (other.functionCode) { ExpressionTreeBuilder tb (functionCode); tb.parseFunctionParamsAndBody (*this); } DynamicObject::Ptr clone() override { return new FunctionObject (*this); } void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/) override { out << "function " << functionCode; } var invoke (const Scope& s, const var::NativeFunctionArgs& args) const { DynamicObject::Ptr functionRoot (new DynamicObject()); static const Identifier thisIdent ("this"); functionRoot->setProperty (thisIdent, args.thisObject); for (int i = 0; i < parameters.size(); ++i) functionRoot->setProperty (parameters.getReference(i), i < args.numArguments ? args.arguments[i] : var::undefined()); var result; body->perform (Scope (&s, s.root, functionRoot), &result); return result; } String functionCode; Array parameters; ScopedPointer body; }; //============================================================================== struct TokenIterator { TokenIterator (const String& code) : location (code), p (code.getCharPointer()) { skip(); } void skip() { skipWhitespaceAndComments(); location.location = p; currentType = matchNextToken(); } void match (TokenType expected) { if (currentType != expected) location.throwError ("Found " + getTokenName (currentType) + " when expecting " + getTokenName (expected)); skip(); } bool matchIf (TokenType expected) { if (currentType == expected) { skip(); return true; } return false; } bool matchesAny (TokenType t1, TokenType t2) const { return currentType == t1 || currentType == t2; } bool matchesAny (TokenType t1, TokenType t2, TokenType t3) const { return matchesAny (t1, t2) || currentType == t3; } CodeLocation location; TokenType currentType; var currentValue; private: String::CharPointerType p; static bool isIdentifierStart (const juce_wchar c) noexcept { return CharacterFunctions::isLetter (c) || c == '_'; } static bool isIdentifierBody (const juce_wchar c) noexcept { return CharacterFunctions::isLetterOrDigit (c) || c == '_'; } TokenType matchNextToken() { if (isIdentifierStart (*p)) { String::CharPointerType end (p); while (isIdentifierBody (*++end)) {} const size_t len = (size_t) (end - p); #define JUCE_JS_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (TokenTypes::name, len)) return TokenTypes::name; JUCE_JS_KEYWORDS (JUCE_JS_COMPARE_KEYWORD) currentValue = String (p, end); p = end; return TokenTypes::identifier; } if (p.isDigit()) { if (parseHexLiteral() || parseFloatLiteral() || parseOctalLiteral() || parseDecimalLiteral()) return TokenTypes::literal; location.throwError ("Syntax error in numeric constant"); } if (parseStringLiteral (*p) || (*p == '.' && parseFloatLiteral())) return TokenTypes::literal; #define JUCE_JS_COMPARE_OPERATOR(name, str) if (matchToken (TokenTypes::name, sizeof (str) - 1)) return TokenTypes::name; JUCE_JS_OPERATORS (JUCE_JS_COMPARE_OPERATOR) if (! p.isEmpty()) location.throwError ("Unexpected character '" + String::charToString (*p) + "' in source"); return TokenTypes::eof; } bool matchToken (TokenType name, const size_t len) noexcept { if (p.compareUpTo (CharPointer_ASCII (name), (int) len) != 0) return false; p += (int) len; return true; } void skipWhitespaceAndComments() { for (;;) { p = p.findEndOfWhitespace(); if (*p == '/') { const juce_wchar c2 = p[1]; if (c2 == '/') { p = CharacterFunctions::find (p, (juce_wchar) '\n'); continue; } if (c2 == '*') { location.location = p; p = CharacterFunctions::find (p + 2, CharPointer_ASCII ("*/")); if (p.isEmpty()) location.throwError ("Unterminated '/*' comment"); p += 2; continue; } } break; } } bool parseStringLiteral (juce_wchar quoteType) { if (quoteType != '"' && quoteType != '\'') return false; Result r (JSON::parseQuotedString (p, currentValue)); if (r.failed()) location.throwError (r.getErrorMessage()); return true; } bool parseHexLiteral() { if (*p != '0' || (p[1] != 'x' && p[1] != 'X')) return false; String::CharPointerType t (++p); int64 v = CharacterFunctions::getHexDigitValue (*++t); if (v < 0) return false; for (;;) { const int digit = CharacterFunctions::getHexDigitValue (*++t); if (digit < 0) break; v = v * 16 + digit; } currentValue = v; p = t; return true; } bool parseFloatLiteral() { int numDigits = 0; String::CharPointerType t (p); while (t.isDigit()) { ++t; ++numDigits; } const bool hasPoint = (*t == '.'); if (hasPoint) while ((++t).isDigit()) ++numDigits; if (numDigits == 0) return false; juce_wchar c = *t; const bool hasExponent = (c == 'e' || c == 'E'); if (hasExponent) { c = *++t; if (c == '+' || c == '-') ++t; if (! t.isDigit()) return false; while ((++t).isDigit()) {} } if (! (hasExponent || hasPoint)) return false; currentValue = CharacterFunctions::getDoubleValue (p); p = t; return true; } bool parseOctalLiteral() { String::CharPointerType t (p); int64 v = *t - '0'; if (v != 0) return false; // first digit of octal must be 0 for (;;) { const int digit = (int) (*++t - '0'); if (isPositiveAndBelow (digit, 8)) v = v * 8 + digit; else if (isPositiveAndBelow (digit, 10)) location.throwError ("Decimal digit in octal constant"); else break; } currentValue = v; p = t; return true; } bool parseDecimalLiteral() { int64 v = 0; for (;; ++p) { const int digit = (int) (*p - '0'); if (isPositiveAndBelow (digit, 10)) v = v * 10 + digit; else break; } currentValue = v; return true; } }; //============================================================================== struct ExpressionTreeBuilder : private TokenIterator { ExpressionTreeBuilder (const String code) : TokenIterator (code) {} BlockStatement* parseStatementList() { ScopedPointer b (new BlockStatement (location)); while (currentType != TokenTypes::closeBrace && currentType != TokenTypes::eof) b->statements.add (parseStatement()); return b.release(); } void parseFunctionParamsAndBody (FunctionObject& fo) { match (TokenTypes::openParen); while (currentType != TokenTypes::closeParen) { fo.parameters.add (currentValue.toString()); match (TokenTypes::identifier); if (currentType != TokenTypes::closeParen) match (TokenTypes::comma); } match (TokenTypes::closeParen); fo.body = parseBlock(); } Expression* parseExpression() { ExpPtr lhs (parseLogicOperator()); if (matchIf (TokenTypes::question)) return parseTerneryOperator (lhs); if (matchIf (TokenTypes::assign)) { ExpPtr rhs (parseExpression()); return new Assignment (location, lhs, rhs); } if (matchIf (TokenTypes::plusEquals)) return parseInPlaceOpExpression (lhs); if (matchIf (TokenTypes::minusEquals)) return parseInPlaceOpExpression (lhs); if (matchIf (TokenTypes::leftShiftEquals)) return parseInPlaceOpExpression (lhs); if (matchIf (TokenTypes::rightShiftEquals)) return parseInPlaceOpExpression (lhs); return lhs.release(); } private: void throwError (const String& err) const { location.throwError (err); } template Expression* parseInPlaceOpExpression (ExpPtr& lhs) { ExpPtr rhs (parseExpression()); Expression* bareLHS = lhs; // careful - bare pointer is deliberately alised return new SelfAssignment (location, bareLHS, new OpType (location, lhs, rhs)); } BlockStatement* parseBlock() { match (TokenTypes::openBrace); ScopedPointer b (parseStatementList()); match (TokenTypes::closeBrace); return b.release(); } Statement* parseStatement() { if (currentType == TokenTypes::openBrace) return parseBlock(); if (matchIf (TokenTypes::var)) return parseVar(); if (matchIf (TokenTypes::if_)) return parseIf(); if (matchIf (TokenTypes::while_)) return parseDoOrWhileLoop (false); if (matchIf (TokenTypes::do_)) return parseDoOrWhileLoop (true); if (matchIf (TokenTypes::for_)) return parseForLoop(); if (matchIf (TokenTypes::return_)) return new ReturnStatement (location, matchIf (TokenTypes::semicolon) ? new Expression (location) : parseExpression()); if (matchIf (TokenTypes::break_)) return new BreakStatement (location); if (matchIf (TokenTypes::continue_)) return new ContinueStatement (location); if (matchIf (TokenTypes::function)) return parseFunction(); if (matchIf (TokenTypes::semicolon)) return new Statement (location); if (matchIf (TokenTypes::plusplus)) return parsePreIncDec(); if (matchIf (TokenTypes::minusminus)) return parsePreIncDec(); if (matchesAny (TokenTypes::openParen, TokenTypes::openBracket)) return matchEndOfStatement (parseFactor()); if (matchesAny (TokenTypes::identifier, TokenTypes::literal, TokenTypes::minus)) return matchEndOfStatement (parseExpression()); throwError ("Found " + getTokenName (currentType) + " when expecting a statement"); return nullptr; } Expression* matchEndOfStatement (Expression* ex) { ExpPtr e (ex); if (currentType != TokenTypes::eof) match (TokenTypes::semicolon); return e.release(); } Expression* matchCloseParen (Expression* ex) { ExpPtr e (ex); match (TokenTypes::closeParen); return e.release(); } Statement* parseIf() { ScopedPointer s (new IfStatement (location)); match (TokenTypes::openParen); s->condition = parseExpression(); match (TokenTypes::closeParen); s->trueBranch = parseStatement(); s->falseBranch = matchIf (TokenTypes::else_) ? parseStatement() : new Statement (location); return s.release(); } Statement* parseVar() { ScopedPointer s (new VarStatement (location)); s->name = parseIdentifier(); s->initialiser = matchIf (TokenTypes::assign) ? parseExpression() : new Expression (location); if (matchIf (TokenTypes::comma)) { ScopedPointer block (new BlockStatement (location)); block->statements.add (s.release()); block->statements.add (parseVar()); return block.release(); } match (TokenTypes::semicolon); return s.release(); } Statement* parseFunction() { Identifier name; var fn = parseFunctionDefinition (name); if (name.isNull()) throwError ("Functions defined at statement-level must have a name"); ExpPtr nm (new UnqualifiedName (location, name)), value (new LiteralValue (location, fn)); return new Assignment (location, nm, value); } Statement* parseForLoop() { ScopedPointer s (new LoopStatement (location, false)); match (TokenTypes::openParen); s->initialiser = parseStatement(); if (matchIf (TokenTypes::semicolon)) s->condition = new LiteralValue (location, true); else { s->condition = parseExpression(); match (TokenTypes::semicolon); } if (matchIf (TokenTypes::closeParen)) s->iterator = new Statement (location); else { s->iterator = parseExpression(); match (TokenTypes::closeParen); } s->body = parseStatement(); return s.release(); } Statement* parseDoOrWhileLoop (bool isDoLoop) { ScopedPointer s (new LoopStatement (location, isDoLoop)); s->initialiser = new Statement (location); s->iterator = new Statement (location); if (isDoLoop) { s->body = parseBlock(); match (TokenTypes::while_); } match (TokenTypes::openParen); s->condition = parseExpression(); match (TokenTypes::closeParen); if (! isDoLoop) s->body = parseStatement(); return s.release(); } Identifier parseIdentifier() { Identifier i; if (currentType == TokenTypes::identifier) i = currentValue.toString(); match (TokenTypes::identifier); return i; } var parseFunctionDefinition (Identifier& functionName) { const String::CharPointerType functionStart (location.location); if (currentType == TokenTypes::identifier) functionName = parseIdentifier(); ScopedPointer fo (new FunctionObject()); parseFunctionParamsAndBody (*fo); fo->functionCode = String (functionStart, location.location); return var (fo.release()); } Expression* parseFunctionCall (FunctionCall* call, ExpPtr& function) { ScopedPointer s (call); s->object = function; match (TokenTypes::openParen); while (currentType != TokenTypes::closeParen) { s->arguments.add (parseExpression()); if (currentType != TokenTypes::closeParen) match (TokenTypes::comma); } return matchCloseParen (s.release()); } Expression* parseSuffixes (Expression* e) { ExpPtr input (e); if (matchIf (TokenTypes::dot)) return parseSuffixes (new DotOperator (location, input, parseIdentifier())); if (currentType == TokenTypes::openParen) return parseSuffixes (parseFunctionCall (new FunctionCall (location), input)); if (matchIf (TokenTypes::openBracket)) { ScopedPointer s (new ArraySubscript (location)); s->object = input; s->index = parseExpression(); match (TokenTypes::closeBracket); return parseSuffixes (s.release()); } if (matchIf (TokenTypes::plusplus)) return parsePostIncDec (input); if (matchIf (TokenTypes::minusminus)) return parsePostIncDec (input); return input.release(); } Expression* parseFactor() { if (currentType == TokenTypes::identifier) return parseSuffixes (new UnqualifiedName (location, parseIdentifier())); if (matchIf (TokenTypes::openParen)) return parseSuffixes (matchCloseParen (parseExpression())); if (matchIf (TokenTypes::true_)) return parseSuffixes (new LiteralValue (location, (int) 1)); if (matchIf (TokenTypes::false_)) return parseSuffixes (new LiteralValue (location, (int) 0)); if (matchIf (TokenTypes::null_)) return parseSuffixes (new LiteralValue (location, var())); if (matchIf (TokenTypes::undefined)) return parseSuffixes (new Expression (location)); if (currentType == TokenTypes::literal) { var v (currentValue); skip(); return parseSuffixes (new LiteralValue (location, v)); } if (matchIf (TokenTypes::openBrace)) { ScopedPointer e (new ObjectDeclaration (location)); while (currentType != TokenTypes::closeBrace) { e->names.add (currentValue.toString()); match ((currentType == TokenTypes::literal && currentValue.isString()) ? TokenTypes::literal : TokenTypes::identifier); match (TokenTypes::colon); e->initialisers.add (parseExpression()); if (currentType != TokenTypes::closeBrace) match (TokenTypes::comma); } match (TokenTypes::closeBrace); return parseSuffixes (e.release()); } if (matchIf (TokenTypes::openBracket)) { ScopedPointer e (new ArrayDeclaration (location)); while (currentType != TokenTypes::closeBracket) { e->values.add (parseExpression()); if (currentType != TokenTypes::closeBracket) match (TokenTypes::comma); } match (TokenTypes::closeBracket); return parseSuffixes (e.release()); } if (matchIf (TokenTypes::function)) { Identifier name; var fn = parseFunctionDefinition (name); if (name.isValid()) throwError ("Inline functions definitions cannot have a name"); return new LiteralValue (location, fn); } if (matchIf (TokenTypes::new_)) { ExpPtr name (new UnqualifiedName (location, parseIdentifier())); while (matchIf (TokenTypes::dot)) name = new DotOperator (location, name, parseIdentifier()); return parseFunctionCall (new NewOperator (location), name); } throwError ("Found " + getTokenName (currentType) + " when expecting an expression"); return nullptr; } template Expression* parsePreIncDec() { Expression* e = parseFactor(); // careful - bare pointer is deliberately alised ExpPtr lhs (e), one (new LiteralValue (location, (int) 1)); return new SelfAssignment (location, e, new OpType (location, lhs, one)); } template Expression* parsePostIncDec (ExpPtr& lhs) { Expression* e = lhs.release(); // careful - bare pointer is deliberately alised ExpPtr lhs2 (e), one (new LiteralValue (location, (int) 1)); return new PostAssignment (location, e, new OpType (location, lhs2, one)); } Expression* parseUnary() { if (matchIf (TokenTypes::minus)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new SubtractionOp (location, a, b); } if (matchIf (TokenTypes::logicalNot)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new EqualsOp (location, a, b); } if (matchIf (TokenTypes::plusplus)) return parsePreIncDec(); if (matchIf (TokenTypes::minusminus)) return parsePreIncDec(); return parseFactor(); } Expression* parseMultiplyDivide() { ExpPtr a (parseUnary()); for (;;) { if (matchIf (TokenTypes::times)) { ExpPtr b (parseUnary()); a = new MultiplyOp (location, a, b); } else if (matchIf (TokenTypes::divide)) { ExpPtr b (parseUnary()); a = new DivideOp (location, a, b); } else if (matchIf (TokenTypes::modulo)) { ExpPtr b (parseUnary()); a = new ModuloOp (location, a, b); } else break; } return a.release(); } Expression* parseAdditionSubtraction() { ExpPtr a (parseMultiplyDivide()); for (;;) { if (matchIf (TokenTypes::plus)) { ExpPtr b (parseMultiplyDivide()); a = new AdditionOp (location, a, b); } else if (matchIf (TokenTypes::minus)) { ExpPtr b (parseMultiplyDivide()); a = new SubtractionOp (location, a, b); } else break; } return a.release(); } Expression* parseShiftOperator() { ExpPtr a (parseAdditionSubtraction()); for (;;) { if (matchIf (TokenTypes::leftShift)) { ExpPtr b (parseExpression()); a = new LeftShiftOp (location, a, b); } else if (matchIf (TokenTypes::rightShift)) { ExpPtr b (parseExpression()); a = new RightShiftOp (location, a, b); } else if (matchIf (TokenTypes::rightShiftUnsigned)) { ExpPtr b (parseExpression()); a = new RightShiftUnsignedOp (location, a, b); } else break; } return a.release(); } Expression* parseComparator() { ExpPtr a (parseShiftOperator()); for (;;) { if (matchIf (TokenTypes::equals)) { ExpPtr b (parseShiftOperator()); a = new EqualsOp (location, a, b); } else if (matchIf (TokenTypes::notEquals)) { ExpPtr b (parseShiftOperator()); a = new NotEqualsOp (location, a, b); } else if (matchIf (TokenTypes::typeEquals)) { ExpPtr b (parseShiftOperator()); a = new TypeEqualsOp (location, a, b); } else if (matchIf (TokenTypes::typeNotEquals)) { ExpPtr b (parseShiftOperator()); a = new TypeNotEqualsOp (location, a, b); } else if (matchIf (TokenTypes::lessThan)) { ExpPtr b (parseShiftOperator()); a = new LessThanOp (location, a, b); } else if (matchIf (TokenTypes::lessThanOrEqual)) { ExpPtr b (parseShiftOperator()); a = new LessThanOrEqualOp (location, a, b); } else if (matchIf (TokenTypes::greaterThan)) { ExpPtr b (parseShiftOperator()); a = new GreaterThanOp (location, a, b); } else if (matchIf (TokenTypes::greaterThanOrEqual)) { ExpPtr b (parseShiftOperator()); a = new GreaterThanOrEqualOp (location, a, b); } else break; } return a.release(); } Expression* parseLogicOperator() { ExpPtr a (parseComparator()); for (;;) { if (matchIf (TokenTypes::logicalAnd)) { ExpPtr b (parseComparator()); a = new LogicalAndOp (location, a, b); } else if (matchIf (TokenTypes::logicalOr)) { ExpPtr b (parseComparator()); a = new LogicalOrOp (location, a, b); } else if (matchIf (TokenTypes::bitwiseAnd)) { ExpPtr b (parseComparator()); a = new BitwiseAndOp (location, a, b); } else if (matchIf (TokenTypes::bitwiseOr)) { ExpPtr b (parseComparator()); a = new BitwiseOrOp (location, a, b); } else if (matchIf (TokenTypes::bitwiseXor)) { ExpPtr b (parseComparator()); a = new BitwiseXorOp (location, a, b); } else break; } return a.release(); } Expression* parseTerneryOperator (ExpPtr& condition) { ScopedPointer e (new ConditionalOp (location)); e->condition = condition; e->trueBranch = parseExpression(); match (TokenTypes::colon); e->falseBranch = parseExpression(); return e.release(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExpressionTreeBuilder) }; //============================================================================== static var get (Args a, int index) noexcept { return index < a.numArguments ? a.arguments[index] : var(); } static bool isInt (Args a, int index) noexcept { return get (a, index).isInt() || get (a, index).isInt64(); } static int getInt (Args a, int index) noexcept { return get (a, index); } static double getDouble (Args a, int index) noexcept { return get (a, index); } static String getString (Args a, int index) noexcept { return get (a, index).toString(); } //============================================================================== struct ObjectClass : public DynamicObject { ObjectClass() { setMethod ("dump", dump); setMethod ("clone", cloneFn); } static Identifier getClassName() { static const Identifier i ("Object"); return i; } static var dump (Args a) { DBG (JSON::toString (a.thisObject)); (void) a; return var::undefined(); } static var cloneFn (Args a) { return a.thisObject.clone(); } }; //============================================================================== struct ArrayClass : public DynamicObject { ArrayClass() { setMethod ("contains", contains); setMethod ("remove", remove); setMethod ("join", join); } static Identifier getClassName() { static const Identifier i ("Array"); return i; } static var contains (Args a) { if (const Array* array = a.thisObject.getArray()) return array->contains (get (a, 0)); return false; } static var remove (Args a) { if (Array* array = a.thisObject.getArray()) array->removeAllInstancesOf (get (a, 0)); return var::undefined(); } static var join (Args a) { StringArray strings; if (const Array* array = a.thisObject.getArray()) for (int i = 0; i < array->size(); ++i) strings.add (array->getReference(i).toString()); return strings.joinIntoString (getString (a, 0)); } }; //============================================================================== struct StringClass : public DynamicObject { StringClass() { setMethod ("substring", substring); setMethod ("indexOf", indexOf); setMethod ("charAt", charAt); setMethod ("charCodeAt", charCodeAt); setMethod ("fromCharCode", fromCharCode); setMethod ("split", split); } static Identifier getClassName() { static const Identifier i ("String"); return i; } static var fromCharCode (Args a) { return String::charToString (getInt (a, 0)); } static var substring (Args a) { return a.thisObject.toString().substring (getInt (a, 0), getInt (a, 1)); } static var indexOf (Args a) { return a.thisObject.toString().indexOf (getString (a, 0)); } static var charCodeAt (Args a) { return (int) a.thisObject.toString() [getInt (a, 0)]; } static var charAt (Args a) { int p = getInt (a, 0); return a.thisObject.toString().substring (p, p + 1); } static var split (Args a) { const String str (a.thisObject.toString()); const String sep (getString (a, 0)); StringArray strings; if (sep.isNotEmpty()) strings.addTokens (str, sep.substring (0, 1), ""); else // special-case for empty separator: split all chars separately for (String::CharPointerType pos = str.getCharPointer(); ! pos.isEmpty(); ++pos) strings.add (String::charToString (*pos)); var array; for (int i = 0; i < strings.size(); ++i) array.append (strings[i]); return array; } }; //============================================================================== struct MathClass : public DynamicObject { MathClass() { setMethod ("abs", Math_abs); setMethod ("round", Math_round); setMethod ("random", Math_random); setMethod ("randInt", Math_randInt); setMethod ("min", Math_min); setMethod ("max", Math_max); setMethod ("range", Math_range); setMethod ("sign", Math_sign); setMethod ("PI", Math_pi); setMethod ("E", Math_e); setMethod ("toDegrees", Math_toDegrees); setMethod ("toRadians", Math_toRadians); setMethod ("sin", Math_sin); setMethod ("asin", Math_asin); setMethod ("sinh", Math_sinh); setMethod ("asinh", Math_asinh); setMethod ("cos", Math_cos); setMethod ("acos", Math_acos); setMethod ("cosh", Math_cosh); setMethod ("acosh", Math_acosh); setMethod ("tan", Math_tan); setMethod ("atan", Math_atan); setMethod ("tanh", Math_tanh); setMethod ("atanh", Math_atanh); setMethod ("log", Math_log); setMethod ("log10", Math_log10); setMethod ("exp", Math_exp); setMethod ("pow", Math_pow); setMethod ("sqr", Math_sqr); setMethod ("sqrt", Math_sqrt); setMethod ("ceil", Math_ceil); setMethod ("floor", Math_floor); } static var Math_pi (Args) { return double_Pi; } static var Math_e (Args) { return exp (1.0); } static var Math_random (Args) { return Random::getSystemRandom().nextDouble(); } static var Math_randInt (Args a) { return Random::getSystemRandom().nextInt (Range (getInt (a, 0), getInt (a, 1))); } static var Math_abs (Args a) { return isInt (a, 0) ? var (std::abs (getInt (a, 0))) : var (std::abs (getDouble (a, 0))); } static var Math_round (Args a) { return isInt (a, 0) ? var (roundToInt (getInt (a, 0))) : var (roundToInt (getDouble (a, 0))); } static var Math_sign (Args a) { return isInt (a, 0) ? var (sign (getInt (a, 0))) : var (sign (getDouble (a, 0))); } static var Math_range (Args a) { return isInt (a, 0) ? var (jlimit (getInt (a, 1), getInt (a, 2), getInt (a, 0))) : var (jlimit (getDouble (a, 1), getDouble (a, 2), getDouble (a, 0))); } static var Math_min (Args a) { return (isInt (a, 0) && isInt (a, 1)) ? var (jmin (getInt (a, 0), getInt (a, 1))) : var (jmin (getDouble (a, 0), getDouble (a, 1))); } static var Math_max (Args a) { return (isInt (a, 0) && isInt (a, 1)) ? var (jmax (getInt (a, 0), getInt (a, 1))) : var (jmax (getDouble (a, 0), getDouble (a, 1))); } static var Math_toDegrees (Args a) { return radiansToDegrees (getDouble (a, 0)); } static var Math_toRadians (Args a) { return degreesToRadians (getDouble (a, 0)); } static var Math_sin (Args a) { return sin (getDouble (a, 0)); } static var Math_asin (Args a) { return asin (getDouble (a, 0)); } static var Math_cos (Args a) { return cos (getDouble (a, 0)); } static var Math_acos (Args a) { return acos (getDouble (a, 0)); } static var Math_sinh (Args a) { return sinh (getDouble (a, 0)); } static var Math_asinh (Args a) { return asinh (getDouble (a, 0)); } static var Math_cosh (Args a) { return cosh (getDouble (a, 0)); } static var Math_acosh (Args a) { return acosh (getDouble (a, 0)); } static var Math_tan (Args a) { return tan (getDouble (a, 0)); } static var Math_tanh (Args a) { return tanh (getDouble (a, 0)); } static var Math_atan (Args a) { return atan (getDouble (a, 0)); } static var Math_atanh (Args a) { return atanh (getDouble (a, 0)); } static var Math_log (Args a) { return log (getDouble (a, 0)); } static var Math_log10 (Args a) { return log10 (getDouble (a, 0)); } static var Math_exp (Args a) { return exp (getDouble (a, 0)); } static var Math_pow (Args a) { return pow (getDouble (a, 0), getDouble (a, 1)); } static var Math_sqr (Args a) { double x = getDouble (a, 0); return x * x; } static var Math_sqrt (Args a) { return std::sqrt (getDouble (a, 0)); } static var Math_ceil (Args a) { return std::ceil (getDouble (a, 0)); } static var Math_floor (Args a) { return std::floor (getDouble (a, 0)); } static Identifier getClassName() { static const Identifier i ("Math"); return i; } template static Type sign (Type n) noexcept { return n > 0 ? (Type) 1 : (n < 0 ? (Type) -1 : 0); } }; //============================================================================== struct JSONClass : public DynamicObject { JSONClass() { setMethod ("stringify", stringify); } static Identifier getClassName() { static const Identifier i ("JSON"); return i; } static var stringify (Args a) { return JSON::toString (get (a, 0)); } }; //============================================================================== struct IntegerClass : public DynamicObject { IntegerClass() { setMethod ("parseInt", parseInt); } static Identifier getClassName() { static const Identifier i ("Integer"); return i; } static var parseInt (Args a) { const String s (getString (a, 0).trim()); return s[0] == '0' ? (s[1] == 'x' ? s.substring(2).getHexValue64() : getOctalValue (s)) : s.getLargeIntValue(); } }; //============================================================================== static var trace (Args a) { Logger::outputDebugString (JSON::toString (a.thisObject)); return var::undefined(); } static var charToInt (Args a) { return (int) (getString (a, 0)[0]); } static var exec (Args a) { if (RootObject* root = dynamic_cast (a.thisObject.getObject())) root->execute (getString (a, 0)); return var::undefined(); } static var eval (Args a) { if (RootObject* root = dynamic_cast (a.thisObject.getObject())) return root->evaluate (getString (a, 0)); return var::undefined(); } }; //============================================================================== JavascriptEngine::JavascriptEngine() : maximumExecutionTime (15.0), root (new RootObject()) { registerNativeObject (RootObject::ObjectClass ::getClassName(), new RootObject::ObjectClass()); registerNativeObject (RootObject::ArrayClass ::getClassName(), new RootObject::ArrayClass()); registerNativeObject (RootObject::StringClass ::getClassName(), new RootObject::StringClass()); registerNativeObject (RootObject::MathClass ::getClassName(), new RootObject::MathClass()); registerNativeObject (RootObject::JSONClass ::getClassName(), new RootObject::JSONClass()); registerNativeObject (RootObject::IntegerClass ::getClassName(), new RootObject::IntegerClass()); } JavascriptEngine::~JavascriptEngine() {} void JavascriptEngine::prepareTimeout() const noexcept { root->timeout = Time::getCurrentTime() + maximumExecutionTime; } void JavascriptEngine::registerNativeObject (const Identifier& name, DynamicObject* object) { root->setProperty (name, object); } Result JavascriptEngine::execute (const String& code) { try { prepareTimeout(); root->execute (code); } catch (String& error) { return Result::fail (error); } return Result::ok(); } var JavascriptEngine::evaluate (const String& code, Result* result) { try { prepareTimeout(); if (result != nullptr) *result = Result::ok(); return root->evaluate (code); } catch (String& error) { if (result != nullptr) *result = Result::fail (error); } return var::undefined(); } var JavascriptEngine::callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* result) { var returnVal (var::undefined()); try { prepareTimeout(); if (result != nullptr) *result = Result::ok(); RootObject::Scope (nullptr, root, root).findAndInvokeMethod (function, args, returnVal); } catch (String& error) { if (result != nullptr) *result = Result::fail (error); } return returnVal; } const NamedValueSet& JavascriptEngine::getRootObjectProperties() const noexcept { return root->getProperties(); } #if JUCE_MSVC #pragma warning (pop) #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h000066400000000000000000000120451320201440200311770ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ /** A simple javascript interpreter! It's not fully standards-compliant, and won't be as fast as the fancy JIT-compiled engines that you get in browsers, but this is an extremely compact, low-overhead javascript interpreter, which is integrated with the juce var and DynamicObject classes. If you need a few simple bits of scripting in your app, and want to be able to easily let the JS work with native objects defined as DynamicObject subclasses, then this might do the job. To use, simply create an instance of this class and call execute() to run your code. Variables that the script sets can be retrieved with evaluate(), and if you need to provide native objects for the script to use, you can add them with registerNativeObject(). One caveat: Because the values and objects that the engine works with are DynamicObject and var objects, they use reference-counting rather than garbage-collection, so if your script creates complex connections between objects, you run the risk of creating cyclic dependencies and hence leaking. */ class JavascriptEngine { public: /** Creates an instance of the engine. This creates a root namespace and defines some basic Object, String, Array and Math library methods. */ JavascriptEngine(); /** Destructor. */ ~JavascriptEngine(); /** Attempts to parse and run a block of javascript code. If there's a parse or execution error, the error description is returned in the result. You can specify a maximum time for which the program is allowed to run, and it'll return with an error message if this time is exceeded. */ Result execute (const String& javascriptCode); /** Attempts to parse and run a javascript expression, and returns the result. If there's a syntax error, or the expression can't be evaluated, the return value will be var::undefined(). The errorMessage parameter gives you a way to find out any parsing errors. You can specify a maximum time for which the program is allowed to run, and it'll return with an error message if this time is exceeded. */ var evaluate (const String& javascriptCode, Result* errorMessage = nullptr); /** Calls a function in the root namespace, and returns the result. The function arguments are passed in the same format as used by native methods in the var class. */ var callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* errorMessage = nullptr); /** Adds a native object to the root namespace. The object passed-in is reference-counted, and will be retained by the engine until the engine is deleted. The name must be a simple JS identifier, without any dots. */ void registerNativeObject (const Identifier& objectName, DynamicObject* object); /** This value indicates how long a call to one of the evaluate methods is permitted to run before timing-out and failing. The default value is a number of seconds, but you can change this to whatever value suits your application. */ RelativeTime maximumExecutionTime; /** Provides access to the set of properties of the root namespace object. */ const NamedValueSet& getRootObjectProperties() const noexcept; private: JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject) const ReferenceCountedObjectPtr root; void prepareTimeout() const noexcept; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/juce_core.cpp000066400000000000000000000174661320201440200262220ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #if defined (JUCE_CORE_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" //============================================================================== #include "native/juce_BasicNativeHeaders.h" #include "juce_core.h" #include #include #if ! JUCE_ANDROID #include #include #endif #if JUCE_WINDOWS #include #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 #include #include #if ! JUCE_MINGW #include #if ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment (lib, "DbgHelp.lib") #endif #endif #if JUCE_MINGW #include #endif #else #if JUCE_LINUX || JUCE_ANDROID #include #include #include #include #include #endif #if JUCE_LINUX #include #include #if JUCE_USE_CURL #include #endif #endif #include #include #include #include #include #include #include #include #if ! JUCE_ANDROID #include #endif #endif #if JUCE_MAC || JUCE_IOS #include #include #endif #if JUCE_ANDROID #include #endif //============================================================================== #ifndef JUCE_STANDALONE_APPLICATION JUCE_COMPILER_WARNING ("Please re-save your Introjucer project with the latest Introjucer version to avoid this warning") #define JUCE_STANDALONE_APPLICATION 0 #endif //============================================================================== namespace juce { #include "containers/juce_AbstractFifo.cpp" #include "containers/juce_NamedValueSet.cpp" #include "containers/juce_PropertySet.cpp" #include "containers/juce_Variant.cpp" #include "files/juce_DirectoryIterator.cpp" #include "files/juce_File.cpp" #include "files/juce_FileInputStream.cpp" #include "files/juce_FileOutputStream.cpp" #include "files/juce_FileSearchPath.cpp" #include "files/juce_TemporaryFile.cpp" #include "javascript/juce_JSON.cpp" #include "javascript/juce_Javascript.cpp" #include "containers/juce_DynamicObject.cpp" #include "logging/juce_FileLogger.cpp" #include "logging/juce_Logger.cpp" #include "maths/juce_BigInteger.cpp" #include "maths/juce_Expression.cpp" #include "maths/juce_Random.cpp" #include "memory/juce_MemoryBlock.cpp" #include "misc/juce_Result.cpp" #include "misc/juce_Uuid.cpp" #include "network/juce_MACAddress.cpp" #include "network/juce_NamedPipe.cpp" #include "network/juce_Socket.cpp" #include "network/juce_IPAddress.cpp" #include "streams/juce_BufferedInputStream.cpp" #include "streams/juce_FileInputSource.cpp" #include "streams/juce_InputStream.cpp" #include "streams/juce_MemoryInputStream.cpp" #include "streams/juce_MemoryOutputStream.cpp" #include "streams/juce_SubregionStream.cpp" #include "system/juce_SystemStats.cpp" #include "text/juce_CharacterFunctions.cpp" #include "text/juce_Identifier.cpp" #include "text/juce_LocalisedStrings.cpp" #include "text/juce_String.cpp" #include "streams/juce_OutputStream.cpp" #include "text/juce_StringArray.cpp" #include "text/juce_StringPairArray.cpp" #include "text/juce_StringPool.cpp" #include "text/juce_TextDiff.cpp" #include "threads/juce_ReadWriteLock.cpp" #include "threads/juce_Thread.cpp" #include "threads/juce_ThreadPool.cpp" #include "threads/juce_TimeSliceThread.cpp" #include "time/juce_PerformanceCounter.cpp" #include "time/juce_RelativeTime.cpp" #include "time/juce_Time.cpp" #include "unit_tests/juce_UnitTest.cpp" #include "xml/juce_XmlDocument.cpp" #include "xml/juce_XmlElement.cpp" #include "zip/juce_GZIPDecompressorInputStream.cpp" #include "zip/juce_GZIPCompressorOutputStream.cpp" #include "zip/juce_ZipFile.cpp" #include "files/juce_FileFilter.cpp" #include "files/juce_WildcardFileFilter.cpp" //============================================================================== #if JUCE_MAC || JUCE_IOS #include "native/juce_osx_ObjCHelpers.h" #endif #if JUCE_ANDROID #include "native/juce_android_JNIHelpers.h" #endif #if ! JUCE_WINDOWS #include "native/juce_posix_SharedCode.h" #include "native/juce_posix_NamedPipe.cpp" #endif //============================================================================== #if JUCE_MAC || JUCE_IOS #include "native/juce_mac_Files.mm" #include "native/juce_mac_Network.mm" #include "native/juce_mac_Strings.mm" #include "native/juce_mac_SystemStats.mm" #include "native/juce_mac_Threads.mm" //============================================================================== #elif JUCE_WINDOWS #include "native/juce_win32_ComSmartPtr.h" #include "native/juce_win32_Files.cpp" #include "native/juce_win32_Network.cpp" #include "native/juce_win32_Registry.cpp" #include "native/juce_win32_SystemStats.cpp" #include "native/juce_win32_Threads.cpp" //============================================================================== #elif JUCE_LINUX #include "native/juce_linux_CommonFile.cpp" #include "native/juce_linux_Files.cpp" #include "native/juce_linux_Network.cpp" #if JUCE_USE_CURL #include "native/juce_curl_Network.cpp" #endif #include "native/juce_linux_SystemStats.cpp" #include "native/juce_linux_Threads.cpp" //============================================================================== #elif JUCE_ANDROID #include "native/juce_linux_CommonFile.cpp" #include "native/juce_android_Files.cpp" #include "native/juce_android_Misc.cpp" #include "native/juce_android_Network.cpp" #include "native/juce_android_SystemStats.cpp" #include "native/juce_android_Threads.cpp" #endif #include "threads/juce_ChildProcess.cpp" #include "threads/juce_HighResolutionTimer.cpp" #include "network/juce_URL.cpp" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/juce_core.h000066400000000000000000000252451320201440200256610ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CORE_H_INCLUDED #define JUCE_CORE_H_INCLUDED #ifndef JUCE_MODULE_AVAILABLE_juce_core /* If you fail to make sure that all your compile units are building JUCE with the same set of option flags, then there's a risk that different compile units will treat the classes as having different memory layouts, leading to very nasty memory corruption errors when they all get linked together. That's why it's best to always include the Introjucer-generated AppConfig.h file before any juce headers. Note that if you do have an AppConfig.h file and hit this warning, it means that it doesn't contain the JUCE_MODULE_AVAILABLE_xxx flags, which are necessary for some inter-module functionality to work correctly. In that case, you should either rebuild your AppConfig.h with the latest introjucer, or fix it manually to contain these flags. */ #ifdef _MSC_VER #pragma message ("Have you included your AppConfig.h file before including the JUCE headers?") #else #warning "Have you included your AppConfig.h file before including the JUCE headers?" #endif #endif #ifdef _MSC_VER #pragma warning (push) // Disable warnings for long class names, padding, and undefined preprocessor definitions. #pragma warning (disable: 4251 4786 4668 4820) #ifdef __INTEL_COMPILER #pragma warning (disable: 1125) #endif #endif //============================================================================== #include "system/juce_TargetPlatform.h" //============================================================================= /** Config: JUCE_FORCE_DEBUG Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, but if you define this value, you can override this to force it to be true or false. */ #ifndef JUCE_FORCE_DEBUG //#define JUCE_FORCE_DEBUG 0 #endif //============================================================================= /** Config: JUCE_LOG_ASSERTIONS If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog() to write a message when an assertion happens. Enabling it will also leave this turned on in release builds. When it's disabled, however, the jassert and jassertfalse macros will not be compiled in a release build. @see jassert, jassertfalse, Logger */ #ifndef JUCE_LOG_ASSERTIONS #if JUCE_ANDROID #define JUCE_LOG_ASSERTIONS 1 #else #define JUCE_LOG_ASSERTIONS 0 #endif #endif //============================================================================= /** Config: JUCE_CHECK_MEMORY_LEAKS Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes. */ #if JUCE_DEBUG && ! defined (JUCE_CHECK_MEMORY_LEAKS) #define JUCE_CHECK_MEMORY_LEAKS 1 #endif //============================================================================= /** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES In a Visual C++ build, this can be used to stop the required system libs being automatically added to the link stage. */ #ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 #endif /** Config: JUCE_INCLUDE_ZLIB_CODE This can be used to disable Juce's embedded 3rd-party zlib code. You might need to tweak this if you're linking to an external zlib library in your app, but for normal apps, this option should be left alone. If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to specify the path where your zlib headers live. */ #ifndef JUCE_INCLUDE_ZLIB_CODE #define JUCE_INCLUDE_ZLIB_CODE 1 #endif #ifndef JUCE_ZLIB_INCLUDE_PATH #define JUCE_ZLIB_INCLUDE_PATH #endif /** Config: JUCE_USE_CURL Enables http/https support via libcurl (Linux only). Enabling this will add an additional run-time dynmic dependency to libcurl. If you disable this then https/ssl support will not be available on linux. */ #ifndef JUCE_USE_CURL #define JUCE_USE_CURL 0 #endif /* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS If enabled, this will add some exception-catching code to forward unhandled exceptions to your JUCEApplicationBase::unhandledException() callback. */ #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 #endif #ifndef JUCE_STRING_UTF_TYPE #define JUCE_STRING_UTF_TYPE 8 #endif //============================================================================= //============================================================================= #include "system/juce_StandardHeader.h" namespace juce { class StringRef; class MemoryBlock; class File; class InputStream; class OutputStream; class DynamicObject; class FileInputStream; class FileOutputStream; class XmlElement; class JSONFormatter; extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger(); extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noexcept; #include "memory/juce_Memory.h" #include "maths/juce_MathsFunctions.h" #include "memory/juce_ByteOrder.h" #include "memory/juce_Atomic.h" #include "text/juce_CharacterFunctions.h" #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4996) #endif #include "text/juce_CharPointer_UTF8.h" #include "text/juce_CharPointer_UTF16.h" #include "text/juce_CharPointer_UTF32.h" #include "text/juce_CharPointer_ASCII.h" #if JUCE_MSVC #pragma warning (pop) #endif #include "text/juce_String.h" #include "text/juce_StringRef.h" #include "logging/juce_Logger.h" #include "memory/juce_LeakedObjectDetector.h" #include "memory/juce_ContainerDeletePolicy.h" #include "memory/juce_HeapBlock.h" #include "memory/juce_MemoryBlock.h" #include "memory/juce_ReferenceCountedObject.h" #include "memory/juce_ScopedPointer.h" #include "memory/juce_OptionalScopedPointer.h" #include "memory/juce_Singleton.h" #include "memory/juce_WeakReference.h" #include "threads/juce_ScopedLock.h" #include "threads/juce_CriticalSection.h" #include "maths/juce_Range.h" #include "maths/juce_NormalisableRange.h" #include "containers/juce_ElementComparator.h" #include "containers/juce_ArrayAllocationBase.h" #include "containers/juce_Array.h" #include "containers/juce_LinkedListPointer.h" #include "containers/juce_OwnedArray.h" #include "containers/juce_ReferenceCountedArray.h" #include "containers/juce_ScopedValueSetter.h" #include "containers/juce_SortedSet.h" #include "containers/juce_SparseSet.h" #include "containers/juce_AbstractFifo.h" #include "text/juce_NewLine.h" #include "text/juce_StringPool.h" #include "text/juce_Identifier.h" #include "text/juce_StringArray.h" #include "text/juce_StringPairArray.h" #include "text/juce_TextDiff.h" #include "text/juce_LocalisedStrings.h" #include "misc/juce_Result.h" #include "containers/juce_Variant.h" #include "containers/juce_NamedValueSet.h" #include "containers/juce_DynamicObject.h" #include "containers/juce_HashMap.h" #include "time/juce_RelativeTime.h" #include "time/juce_Time.h" #include "streams/juce_InputStream.h" #include "streams/juce_OutputStream.h" #include "streams/juce_BufferedInputStream.h" #include "streams/juce_MemoryInputStream.h" #include "streams/juce_MemoryOutputStream.h" #include "streams/juce_SubregionStream.h" #include "streams/juce_InputSource.h" #include "files/juce_File.h" #include "files/juce_DirectoryIterator.h" #include "files/juce_FileInputStream.h" #include "files/juce_FileOutputStream.h" #include "files/juce_FileSearchPath.h" #include "files/juce_MemoryMappedFile.h" #include "files/juce_TemporaryFile.h" #include "files/juce_FileFilter.h" #include "files/juce_WildcardFileFilter.h" #include "streams/juce_FileInputSource.h" #include "logging/juce_FileLogger.h" #include "javascript/juce_JSON.h" #include "javascript/juce_Javascript.h" #include "maths/juce_BigInteger.h" #include "maths/juce_Expression.h" #include "maths/juce_Random.h" #include "misc/juce_Uuid.h" #include "misc/juce_WindowsRegistry.h" #include "system/juce_SystemStats.h" #include "threads/juce_ChildProcess.h" #include "threads/juce_DynamicLibrary.h" #include "threads/juce_HighResolutionTimer.h" #include "threads/juce_InterProcessLock.h" #include "threads/juce_Process.h" #include "threads/juce_SpinLock.h" #include "threads/juce_WaitableEvent.h" #include "threads/juce_Thread.h" #include "threads/juce_ThreadLocalValue.h" #include "threads/juce_ThreadPool.h" #include "threads/juce_TimeSliceThread.h" #include "threads/juce_ReadWriteLock.h" #include "threads/juce_ScopedReadLock.h" #include "threads/juce_ScopedWriteLock.h" #include "network/juce_IPAddress.h" #include "network/juce_MACAddress.h" #include "network/juce_NamedPipe.h" #include "network/juce_Socket.h" #include "network/juce_URL.h" #include "time/juce_PerformanceCounter.h" #include "unit_tests/juce_UnitTest.h" #include "xml/juce_XmlDocument.h" #include "xml/juce_XmlElement.h" #include "zip/juce_GZIPCompressorOutputStream.h" #include "zip/juce_GZIPDecompressorInputStream.h" #include "zip/juce_ZipFile.h" #include "containers/juce_PropertySet.h" #include "memory/juce_SharedResourcePointer.h" } #if JUCE_MSVC #pragma warning (pop) #endif #endif // JUCE_CORE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/juce_core.mm000066400000000000000000000026331320201440200260370ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #include "juce_core.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/juce_module_info000066400000000000000000000026021320201440200267730ustar00rootroot00000000000000{ "id": "juce_core", "name": "JUCE core classes", "version": "3.2.0", "description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", "website": "http://www.juce.com/juce", "license": "ISC Permissive", "dependencies": [], "include": "juce_core.h", "compile": [ { "file": "juce_core.cpp", "target": "! xcode" }, { "file": "juce_core.mm", "target": "xcode" } ], "browse": [ "text/*", "maths/*", "memory/*", "containers/*", "threads/*", "time/*", "files/*", "network/*", "streams/*", "logging/*", "system/*", "xml/*", "javascript/*", "zip/*", "unit_tests/*", "misc/*", "native/*" ], "OSXFrameworks": "Cocoa IOKit", "iOSFrameworks": "Foundation", "LinuxLibs": "rt dl pthread", "mingwLibs": "uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/logging/000077500000000000000000000000001320201440200251705ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp000066400000000000000000000120271320201440200307230ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ FileLogger::FileLogger (const File& file, const String& welcomeMessage, const int64 maxInitialFileSizeBytes) : logFile (file) { if (maxInitialFileSizeBytes >= 0) trimFileSize (logFile, maxInitialFileSizeBytes); if (! file.exists()) file.create(); // (to create the parent directories) String welcome; welcome << newLine << "**********************************************************" << newLine << welcomeMessage << newLine << "Log started: " << Time::getCurrentTime().toString (true, true) << newLine; FileLogger::logMessage (welcome); } FileLogger::~FileLogger() {} //============================================================================== void FileLogger::logMessage (const String& message) { const ScopedLock sl (logLock); DBG (message); FileOutputStream out (logFile, 256); out << message << newLine; } void FileLogger::trimFileSize (const File& file, int64 maxFileSizeBytes) { if (maxFileSizeBytes <= 0) { file.deleteFile(); } else { const int64 fileSize = file.getSize(); if (fileSize > maxFileSizeBytes) { TemporaryFile tempFile (file); { FileOutputStream out (tempFile.getFile()); FileInputStream in (file); if (! (out.openedOk() && in.openedOk())) return; in.setPosition (fileSize - maxFileSizeBytes); for (;;) { const char c = in.readByte(); if (c == 0) return; if (c == '\n' || c == '\r') { out << c; break; } } out.writeFromInputStream (in, -1); } tempFile.overwriteTargetFileWithTemporary(); } } } //============================================================================== File FileLogger::getSystemLogFileFolder() { #if JUCE_MAC return File ("~/Library/Logs"); #else return File::getSpecialLocation (File::userApplicationDataDirectory); #endif } FileLogger* FileLogger::createDefaultAppLogger (const String& logFileSubDirectoryName, const String& logFileName, const String& welcomeMessage, const int64 maxInitialFileSizeBytes) { return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName) .getChildFile (logFileName), welcomeMessage, maxInitialFileSizeBytes); } FileLogger* FileLogger::createDateStampedLogger (const String& logFileSubDirectoryName, const String& logFileNameRoot, const String& logFileNameSuffix, const String& welcomeMessage) { return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName) .getChildFile (logFileNameRoot + Time::getCurrentTime().formatted ("%Y-%m-%d_%H-%M-%S")) .withFileExtension (logFileNameSuffix) .getNonexistentSibling(), welcomeMessage, 0); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h000066400000000000000000000164071320201440200303760ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_FILELOGGER_H_INCLUDED #define JUCE_FILELOGGER_H_INCLUDED //============================================================================== /** A simple implementation of a Logger that writes to a file. @see Logger */ class JUCE_API FileLogger : public Logger { public: //============================================================================== /** Creates a FileLogger for a given file. @param fileToWriteTo the file that to use - new messages will be appended to the file. If the file doesn't exist, it will be created, along with any parent directories that are needed. @param welcomeMessage when opened, the logger will write a header to the log, along with the current date and time, and this welcome message @param maxInitialFileSizeBytes if this is zero or greater, then if the file already exists but is larger than this number of bytes, then the start of the file will be truncated to keep the size down. This prevents a log file getting ridiculously large over time. The file will be truncated at a new-line boundary. If this value is less than zero, no size limit will be imposed; if it's zero, the file will always be deleted. Note that the size is only checked once when this object is created - any logging that is done later will be appended without any checking */ FileLogger (const File& fileToWriteTo, const String& welcomeMessage, const int64 maxInitialFileSizeBytes = 128 * 1024); /** Destructor. */ ~FileLogger(); //============================================================================== /** Returns the file that this logger is writing to. */ const File& getLogFile() const noexcept { return logFile; } //============================================================================== /** Helper function to create a log file in the correct place for this platform. The method might return nullptr if the file can't be created for some reason. @param logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as returned by getSystemLogFileFolder). It's best to use something like the name of your application here. @param logFileName the name of the file to create, e.g. "MyAppLog.txt". @param welcomeMessage a message that will be written to the log when it's opened. @param maxInitialFileSizeBytes (see the FileLogger constructor for more info on this) */ static FileLogger* createDefaultAppLogger (const String& logFileSubDirectoryName, const String& logFileName, const String& welcomeMessage, const int64 maxInitialFileSizeBytes = 128 * 1024); /** Helper function to create a log file in the correct place for this platform. The filename used is based on the root and suffix strings provided, along with a time and date string, meaning that a new, empty log file will be always be created rather than appending to an exising one. The method might return nullptr if the file can't be created for some reason. @param logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as returned by getSystemLogFileFolder). It's best to use something like the name of your application here. @param logFileNameRoot the start of the filename to use, e.g. "MyAppLog_". This will have a timestamp and the logFileNameSuffix appended to it @param logFileNameSuffix the file suffix to use, e.g. ".txt" @param welcomeMessage a message that will be written to the log when it's opened. */ static FileLogger* createDateStampedLogger (const String& logFileSubDirectoryName, const String& logFileNameRoot, const String& logFileNameSuffix, const String& welcomeMessage); //============================================================================== /** Returns an OS-specific folder where log-files should be stored. On Windows this will return a logger with a path such as: c:\\Documents and Settings\\username\\Application Data\\[logFileSubDirectoryName]\\[logFileName] On the Mac it'll create something like: ~/Library/Logs/[logFileSubDirectoryName]/[logFileName] @see createDefaultAppLogger */ static File getSystemLogFileFolder(); // (implementation of the Logger virtual method) void logMessage (const String&); //============================================================================== /** This is a utility function which removes lines from the start of a text file to make sure that its total size is below the given size. */ static void trimFileSize (const File& file, int64 maxFileSize); private: //============================================================================== File logFile; CriticalSection logLock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileLogger) }; #endif // JUCE_FILELOGGER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp000066400000000000000000000046351320201440200301310ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ Logger::Logger() {} Logger::~Logger() { // You're deleting this logger while it's still being used! // Always call Logger::setCurrentLogger (nullptr) before deleting the active logger. jassert (currentLogger != this); } Logger* Logger::currentLogger = nullptr; void Logger::setCurrentLogger (Logger* const newLogger) noexcept { currentLogger = newLogger; } Logger* Logger::getCurrentLogger() noexcept { return currentLogger; } void Logger::writeToLog (const String& message) { if (currentLogger != nullptr) currentLogger->logMessage (message); else outputDebugString (message); } #if JUCE_LOG_ASSERTIONS || JUCE_DEBUG void JUCE_API JUCE_CALLTYPE logAssertion (const char* const filename, const int lineNum) noexcept { String m ("JUCE Assertion failure in "); m << File::createFileWithoutCheckingPath (filename).getFileName() << ':' << lineNum; #if JUCE_LOG_ASSERTIONS Logger::writeToLog (m); #else DBG (m); #endif } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h000066400000000000000000000071431320201440200275730ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_LOGGER_H_INCLUDED #define JUCE_LOGGER_H_INCLUDED //============================================================================== /** Acts as an application-wide logging class. A subclass of Logger can be created and passed into the Logger::setCurrentLogger method and this will then be used by all calls to writeToLog. The logger class also contains methods for writing messages to the debugger's output stream. @see FileLogger */ class JUCE_API Logger { public: //============================================================================== /** Destructor. */ virtual ~Logger(); //============================================================================== /** Sets the current logging class to use. Note that the object passed in will not be owned or deleted by the logger, so the caller must make sure that it is not deleted while still being used. A null pointer can be passed-in to disable any logging. */ static void JUCE_CALLTYPE setCurrentLogger (Logger* newLogger) noexcept; /** Returns the current logger, or nullptr if none has been set. */ static Logger* getCurrentLogger() noexcept; /** Writes a string to the current logger. This will pass the string to the logger's logMessage() method if a logger has been set. @see logMessage */ static void JUCE_CALLTYPE writeToLog (const String& message); //============================================================================== /** Writes a message to the standard error stream. This can be called directly, or by using the DBG() macro in juce_PlatformDefs.h (which will avoid calling the method in non-debug builds). */ static void JUCE_CALLTYPE outputDebugString (const String& text); protected: //============================================================================== Logger(); /** This is overloaded by subclasses to implement custom logging behaviour. @see setCurrentLogger */ virtual void logMessage (const String& message) = 0; private: static Logger* currentLogger; }; #endif // JUCE_LOGGER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/000077500000000000000000000000001320201440200246565ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp000066400000000000000000000643771320201440200304300ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ namespace { inline size_t bitToIndex (const int bit) noexcept { return (size_t) (bit >> 5); } inline uint32 bitToMask (const int bit) noexcept { return (uint32) 1 << (bit & 31); } } //============================================================================== BigInteger::BigInteger() : numValues (4), highestBit (-1), negative (false) { values.calloc (numValues + 1); } BigInteger::BigInteger (const int32 value) : numValues (4), highestBit (31), negative (value < 0) { values.calloc (numValues + 1); values[0] = (uint32) abs (value); highestBit = getHighestBit(); } BigInteger::BigInteger (const uint32 value) : numValues (4), highestBit (31), negative (false) { values.calloc (numValues + 1); values[0] = value; highestBit = getHighestBit(); } BigInteger::BigInteger (int64 value) : numValues (4), highestBit (63), negative (value < 0) { values.calloc (numValues + 1); if (value < 0) value = -value; values[0] = (uint32) value; values[1] = (uint32) (value >> 32); highestBit = getHighestBit(); } BigInteger::BigInteger (const BigInteger& other) : numValues ((size_t) jmax ((size_t) 4, bitToIndex (other.highestBit) + 1)), highestBit (other.getHighestBit()), negative (other.negative) { values.malloc (numValues + 1); memcpy (values, other.values, sizeof (uint32) * (numValues + 1)); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS BigInteger::BigInteger (BigInteger&& other) noexcept : values (static_cast &&> (other.values)), numValues (other.numValues), highestBit (other.highestBit), negative (other.negative) { } BigInteger& BigInteger::operator= (BigInteger&& other) noexcept { values = static_cast &&> (other.values); numValues = other.numValues; highestBit = other.highestBit; negative = other.negative; return *this; } #endif BigInteger::~BigInteger() { } void BigInteger::swapWith (BigInteger& other) noexcept { values.swapWith (other.values); std::swap (numValues, other.numValues); std::swap (highestBit, other.highestBit); std::swap (negative, other.negative); } BigInteger& BigInteger::operator= (const BigInteger& other) { if (this != &other) { highestBit = other.getHighestBit(); jassert (other.numValues >= 4); numValues = (size_t) jmax ((size_t) 4, bitToIndex (highestBit) + 1); negative = other.negative; values.malloc (numValues + 1); memcpy (values, other.values, sizeof (uint32) * (numValues + 1)); } return *this; } void BigInteger::ensureSize (const size_t numVals) { if (numVals + 2 >= numValues) { size_t oldSize = numValues; numValues = ((numVals + 2) * 3) / 2; values.realloc (numValues + 1); while (oldSize < numValues) values [oldSize++] = 0; } } //============================================================================== bool BigInteger::operator[] (const int bit) const noexcept { return bit <= highestBit && bit >= 0 && ((values [bitToIndex (bit)] & bitToMask (bit)) != 0); } int BigInteger::toInteger() const noexcept { const int n = (int) (values[0] & 0x7fffffff); return negative ? -n : n; } int64 BigInteger::toInt64() const noexcept { const int64 n = (((int64) (values[1] & 0x7fffffff)) << 32) | values[0]; return negative ? -n : n; } BigInteger BigInteger::getBitRange (int startBit, int numBits) const { BigInteger r; numBits = jmin (numBits, getHighestBit() + 1 - startBit); r.ensureSize ((size_t) bitToIndex (numBits)); r.highestBit = numBits; int i = 0; while (numBits > 0) { r.values[i++] = getBitRangeAsInt (startBit, (int) jmin (32, numBits)); numBits -= 32; startBit += 32; } r.highestBit = r.getHighestBit(); return r; } uint32 BigInteger::getBitRangeAsInt (const int startBit, int numBits) const noexcept { if (numBits > 32) { jassertfalse; // use getBitRange() if you need more than 32 bits.. numBits = 32; } numBits = jmin (numBits, highestBit + 1 - startBit); if (numBits <= 0) return 0; const size_t pos = bitToIndex (startBit); const int offset = startBit & 31; const int endSpace = 32 - numBits; uint32 n = ((uint32) values [pos]) >> offset; if (offset > endSpace) n |= ((uint32) values [pos + 1]) << (32 - offset); return n & (((uint32) 0xffffffff) >> endSpace); } void BigInteger::setBitRangeAsInt (const int startBit, int numBits, uint32 valueToSet) { if (numBits > 32) { jassertfalse; numBits = 32; } for (int i = 0; i < numBits; ++i) { setBit (startBit + i, (valueToSet & 1) != 0); valueToSet >>= 1; } } //============================================================================== void BigInteger::clear() { if (numValues > 16) { numValues = 4; values.calloc (numValues + 1); } else { values.clear (numValues + 1); } highestBit = -1; negative = false; } void BigInteger::setBit (const int bit) { if (bit >= 0) { if (bit > highestBit) { ensureSize (bitToIndex (bit)); highestBit = bit; } values [bitToIndex (bit)] |= bitToMask (bit); } } void BigInteger::setBit (const int bit, const bool shouldBeSet) { if (shouldBeSet) setBit (bit); else clearBit (bit); } void BigInteger::clearBit (const int bit) noexcept { if (bit >= 0 && bit <= highestBit) values [bitToIndex (bit)] &= ~bitToMask (bit); } void BigInteger::setRange (int startBit, int numBits, const bool shouldBeSet) { while (--numBits >= 0) setBit (startBit++, shouldBeSet); } void BigInteger::insertBit (const int bit, const bool shouldBeSet) { if (bit >= 0) shiftBits (1, bit); setBit (bit, shouldBeSet); } //============================================================================== bool BigInteger::isZero() const noexcept { return getHighestBit() < 0; } bool BigInteger::isOne() const noexcept { return getHighestBit() == 0 && ! negative; } bool BigInteger::isNegative() const noexcept { return negative && ! isZero(); } void BigInteger::setNegative (const bool neg) noexcept { negative = neg; } void BigInteger::negate() noexcept { negative = (! negative) && ! isZero(); } #if JUCE_USE_MSVC_INTRINSICS && ! defined (__INTEL_COMPILER) #pragma intrinsic (_BitScanReverse) #endif inline static int highestBitInInt (uint32 n) noexcept { jassert (n != 0); // (the built-in functions may not work for n = 0) #if JUCE_GCC return 31 - __builtin_clz (n); #elif JUCE_USE_MSVC_INTRINSICS unsigned long highest; _BitScanReverse (&highest, n); return (int) highest; #else n |= (n >> 1); n |= (n >> 2); n |= (n >> 4); n |= (n >> 8); n |= (n >> 16); return countBitsInInt32 (n >> 1); #endif } int BigInteger::countNumberOfSetBits() const noexcept { int total = 0; for (int i = (int) bitToIndex (highestBit) + 1; --i >= 0;) total += countNumberOfBits (values[i]); return total; } int BigInteger::getHighestBit() const noexcept { for (int i = (int) bitToIndex (highestBit + 1); i >= 0; --i) { const uint32 n = values[i]; if (n != 0) return highestBitInInt (n) + (i << 5); } return -1; } int BigInteger::findNextSetBit (int i) const noexcept { for (; i <= highestBit; ++i) if ((values [bitToIndex (i)] & bitToMask (i)) != 0) return i; return -1; } int BigInteger::findNextClearBit (int i) const noexcept { for (; i <= highestBit; ++i) if ((values [bitToIndex (i)] & bitToMask (i)) == 0) break; return i; } //============================================================================== BigInteger& BigInteger::operator+= (const BigInteger& other) { if (other.isNegative()) return operator-= (-other); if (isNegative()) { if (compareAbsolute (other) < 0) { BigInteger temp (*this); temp.negate(); *this = other; operator-= (temp); } else { negate(); operator-= (other); negate(); } } else { if (other.highestBit > highestBit) highestBit = other.highestBit; ++highestBit; const size_t numInts = bitToIndex (highestBit) + 1; ensureSize (numInts); int64 remainder = 0; for (size_t i = 0; i <= numInts; ++i) { if (i < numValues) remainder += values[i]; if (i < other.numValues) remainder += other.values[i]; values[i] = (uint32) remainder; remainder >>= 32; } jassert (remainder == 0); highestBit = getHighestBit(); } return *this; } BigInteger& BigInteger::operator-= (const BigInteger& other) { if (other.isNegative()) return operator+= (-other); if (! isNegative()) { if (compareAbsolute (other) < 0) { BigInteger temp (other); swapWith (temp); operator-= (temp); negate(); return *this; } } else { negate(); operator+= (other); negate(); return *this; } const size_t numInts = bitToIndex (highestBit) + 1; const size_t maxOtherInts = bitToIndex (other.highestBit) + 1; int64 amountToSubtract = 0; for (size_t i = 0; i <= numInts; ++i) { if (i <= maxOtherInts) amountToSubtract += (int64) other.values[i]; if (values[i] >= amountToSubtract) { values[i] = (uint32) (values[i] - amountToSubtract); amountToSubtract = 0; } else { const int64 n = ((int64) values[i] + (((int64) 1) << 32)) - amountToSubtract; values[i] = (uint32) n; amountToSubtract = 1; } } return *this; } BigInteger& BigInteger::operator*= (const BigInteger& other) { BigInteger total; highestBit = getHighestBit(); const bool wasNegative = isNegative(); setNegative (false); for (int i = 0; i <= highestBit; ++i) { if (operator[](i)) { BigInteger n (other); n.setNegative (false); n <<= i; total += n; } } total.setNegative (wasNegative ^ other.isNegative()); swapWith (total); return *this; } void BigInteger::divideBy (const BigInteger& divisor, BigInteger& remainder) { jassert (this != &remainder); // (can't handle passing itself in to get the remainder) const int divHB = divisor.getHighestBit(); const int ourHB = getHighestBit(); if (divHB < 0 || ourHB < 0) { // division by zero remainder.clear(); clear(); } else { const bool wasNegative = isNegative(); swapWith (remainder); remainder.setNegative (false); clear(); BigInteger temp (divisor); temp.setNegative (false); int leftShift = ourHB - divHB; temp <<= leftShift; while (leftShift >= 0) { if (remainder.compareAbsolute (temp) >= 0) { remainder -= temp; setBit (leftShift); } if (--leftShift >= 0) temp >>= 1; } negative = wasNegative ^ divisor.isNegative(); remainder.setNegative (wasNegative); } } BigInteger& BigInteger::operator/= (const BigInteger& other) { BigInteger remainder; divideBy (other, remainder); return *this; } BigInteger& BigInteger::operator|= (const BigInteger& other) { // this operation doesn't take into account negative values.. jassert (isNegative() == other.isNegative()); if (other.highestBit >= 0) { ensureSize (bitToIndex (other.highestBit)); int n = (int) bitToIndex (other.highestBit) + 1; while (--n >= 0) values[n] |= other.values[n]; if (other.highestBit > highestBit) highestBit = other.highestBit; highestBit = getHighestBit(); } return *this; } BigInteger& BigInteger::operator&= (const BigInteger& other) { // this operation doesn't take into account negative values.. jassert (isNegative() == other.isNegative()); int n = (int) numValues; while (n > (int) other.numValues) values[--n] = 0; while (--n >= 0) values[n] &= other.values[n]; if (other.highestBit < highestBit) highestBit = other.highestBit; highestBit = getHighestBit(); return *this; } BigInteger& BigInteger::operator^= (const BigInteger& other) { // this operation will only work with the absolute values jassert (isNegative() == other.isNegative()); if (other.highestBit >= 0) { ensureSize (bitToIndex (other.highestBit)); int n = (int) bitToIndex (other.highestBit) + 1; while (--n >= 0) values[n] ^= other.values[n]; if (other.highestBit > highestBit) highestBit = other.highestBit; highestBit = getHighestBit(); } return *this; } BigInteger& BigInteger::operator%= (const BigInteger& divisor) { BigInteger remainder; divideBy (divisor, remainder); swapWith (remainder); return *this; } BigInteger& BigInteger::operator++() { return operator+= (1); } BigInteger& BigInteger::operator--() { return operator-= (1); } BigInteger BigInteger::operator++ (int) { const BigInteger old (*this); operator+= (1); return old; } BigInteger BigInteger::operator-- (int) { const BigInteger old (*this); operator-= (1); return old; } BigInteger BigInteger::operator-() const { BigInteger b (*this); b.negate(); return b; } BigInteger BigInteger::operator+ (const BigInteger& other) const { BigInteger b (*this); return b += other; } BigInteger BigInteger::operator- (const BigInteger& other) const { BigInteger b (*this); return b -= other; } BigInteger BigInteger::operator* (const BigInteger& other) const { BigInteger b (*this); return b *= other; } BigInteger BigInteger::operator/ (const BigInteger& other) const { BigInteger b (*this); return b /= other; } BigInteger BigInteger::operator| (const BigInteger& other) const { BigInteger b (*this); return b |= other; } BigInteger BigInteger::operator& (const BigInteger& other) const { BigInteger b (*this); return b &= other; } BigInteger BigInteger::operator^ (const BigInteger& other) const { BigInteger b (*this); return b ^= other; } BigInteger BigInteger::operator% (const BigInteger& other) const { BigInteger b (*this); return b %= other; } BigInteger BigInteger::operator<< (const int numBits) const { BigInteger b (*this); return b <<= numBits; } BigInteger BigInteger::operator>> (const int numBits) const { BigInteger b (*this); return b >>= numBits; } BigInteger& BigInteger::operator<<= (const int numBits) { shiftBits (numBits, 0); return *this; } BigInteger& BigInteger::operator>>= (const int numBits) { shiftBits (-numBits, 0); return *this; } //============================================================================== int BigInteger::compare (const BigInteger& other) const noexcept { if (isNegative() == other.isNegative()) { const int absComp = compareAbsolute (other); return isNegative() ? -absComp : absComp; } else { return isNegative() ? -1 : 1; } } int BigInteger::compareAbsolute (const BigInteger& other) const noexcept { const int h1 = getHighestBit(); const int h2 = other.getHighestBit(); if (h1 > h2) return 1; else if (h1 < h2) return -1; for (int i = (int) bitToIndex (h1) + 1; --i >= 0;) if (values[i] != other.values[i]) return (values[i] > other.values[i]) ? 1 : -1; return 0; } bool BigInteger::operator== (const BigInteger& other) const noexcept { return compare (other) == 0; } bool BigInteger::operator!= (const BigInteger& other) const noexcept { return compare (other) != 0; } bool BigInteger::operator< (const BigInteger& other) const noexcept { return compare (other) < 0; } bool BigInteger::operator<= (const BigInteger& other) const noexcept { return compare (other) <= 0; } bool BigInteger::operator> (const BigInteger& other) const noexcept { return compare (other) > 0; } bool BigInteger::operator>= (const BigInteger& other) const noexcept { return compare (other) >= 0; } //============================================================================== void BigInteger::shiftLeft (int bits, const int startBit) { if (startBit > 0) { for (int i = highestBit + 1; --i >= startBit;) setBit (i + bits, operator[] (i)); while (--bits >= 0) clearBit (bits + startBit); } else { ensureSize (bitToIndex (highestBit + bits) + 1); const size_t wordsToMove = bitToIndex (bits); size_t top = 1 + bitToIndex (highestBit); highestBit += bits; if (wordsToMove > 0) { for (int i = (int) top; --i >= 0;) values [(size_t) i + wordsToMove] = values [i]; for (size_t j = 0; j < wordsToMove; ++j) values [j] = 0; bits &= 31; } if (bits != 0) { const int invBits = 32 - bits; for (size_t i = top + 1 + wordsToMove; --i > wordsToMove;) values[i] = (values[i] << bits) | (values [i - 1] >> invBits); values [wordsToMove] = values [wordsToMove] << bits; } highestBit = getHighestBit(); } } void BigInteger::shiftRight (int bits, const int startBit) { if (startBit > 0) { for (int i = startBit; i <= highestBit; ++i) setBit (i, operator[] (i + bits)); highestBit = getHighestBit(); } else { if (bits > highestBit) { clear(); } else { const size_t wordsToMove = bitToIndex (bits); size_t top = 1 + bitToIndex (highestBit) - wordsToMove; highestBit -= bits; if (wordsToMove > 0) { size_t i; for (i = 0; i < top; ++i) values [i] = values [i + wordsToMove]; for (i = 0; i < wordsToMove; ++i) values [top + i] = 0; bits &= 31; } if (bits != 0) { const int invBits = 32 - bits; --top; for (size_t i = 0; i < top; ++i) values[i] = (values[i] >> bits) | (values [i + 1] << invBits); values[top] = (values[top] >> bits); } highestBit = getHighestBit(); } } } void BigInteger::shiftBits (int bits, const int startBit) { if (highestBit >= 0) { if (bits < 0) shiftRight (-bits, startBit); else if (bits > 0) shiftLeft (bits, startBit); } } //============================================================================== static BigInteger simpleGCD (BigInteger* m, BigInteger* n) { while (! m->isZero()) { if (n->compareAbsolute (*m) > 0) std::swap (m, n); *m -= *n; } return *n; } BigInteger BigInteger::findGreatestCommonDivisor (BigInteger n) const { BigInteger m (*this); while (! n.isZero()) { if (abs (m.getHighestBit() - n.getHighestBit()) <= 16) return simpleGCD (&m, &n); BigInteger temp2; m.divideBy (n, temp2); m.swapWith (n); n.swapWith (temp2); } return m; } void BigInteger::exponentModulo (const BigInteger& exponent, const BigInteger& modulus) { BigInteger exp (exponent); exp %= modulus; BigInteger value (1); swapWith (value); value %= modulus; while (! exp.isZero()) { if (exp [0]) { operator*= (value); operator%= (modulus); } value *= value; value %= modulus; exp >>= 1; } } void BigInteger::inverseModulo (const BigInteger& modulus) { if (modulus.isOne() || modulus.isNegative()) { clear(); return; } if (isNegative() || compareAbsolute (modulus) >= 0) operator%= (modulus); if (isOne()) return; if (! (*this)[0]) { // not invertible clear(); return; } BigInteger a1 (modulus); BigInteger a2 (*this); BigInteger b1 (modulus); BigInteger b2 (1); while (! a2.isOne()) { BigInteger temp1, multiplier (a1); multiplier.divideBy (a2, temp1); temp1 = a2; temp1 *= multiplier; BigInteger temp2 (a1); temp2 -= temp1; a1 = a2; a2 = temp2; temp1 = b2; temp1 *= multiplier; temp2 = b1; temp2 -= temp1; b1 = b2; b2 = temp2; } while (b2.isNegative()) b2 += modulus; b2 %= modulus; swapWith (b2); } //============================================================================== OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const BigInteger& value) { return stream << value.toString (10); } String BigInteger::toString (const int base, const int minimumNumCharacters) const { String s; BigInteger v (*this); if (base == 2 || base == 8 || base == 16) { const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4); static const char hexDigits[] = "0123456789abcdef"; for (;;) { const uint32 remainder = v.getBitRangeAsInt (0, bits); v >>= bits; if (remainder == 0 && v.isZero()) break; s = String::charToString ((juce_wchar) (uint8) hexDigits [remainder]) + s; } } else if (base == 10) { const BigInteger ten (10); BigInteger remainder; for (;;) { v.divideBy (ten, remainder); if (remainder.isZero() && v.isZero()) break; s = String (remainder.getBitRangeAsInt (0, 8)) + s; } } else { jassertfalse; // can't do the specified base! return String(); } s = s.paddedLeft ('0', minimumNumCharacters); return isNegative() ? "-" + s : s; } void BigInteger::parseString (StringRef text, const int base) { clear(); String::CharPointerType t (text.text.findEndOfWhitespace()); setNegative (*t == (juce_wchar) '-'); if (base == 2 || base == 8 || base == 16) { const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4); for (;;) { const juce_wchar c = t.getAndAdvance(); const int digit = CharacterFunctions::getHexDigitValue (c); if (((uint32) digit) < (uint32) base) { operator<<= (bits); operator+= (digit); } else if (c == 0) { break; } } } else if (base == 10) { const BigInteger ten ((uint32) 10); for (;;) { const juce_wchar c = t.getAndAdvance(); if (c >= '0' && c <= '9') { operator*= (ten); operator+= ((int) (c - '0')); } else if (c == 0) { break; } } } } MemoryBlock BigInteger::toMemoryBlock() const { const int numBytes = (getHighestBit() + 8) >> 3; MemoryBlock mb ((size_t) numBytes); for (int i = 0; i < numBytes; ++i) mb[i] = (char) getBitRangeAsInt (i << 3, 8); return mb; } void BigInteger::loadFromMemoryBlock (const MemoryBlock& data) { clear(); for (int i = (int) data.getSize(); --i >= 0;) this->setBitRangeAsInt (i << 3, 8, (uint32) data [i]); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h000066400000000000000000000314161320201440200300610ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_BIGINTEGER_H_INCLUDED #define JUCE_BIGINTEGER_H_INCLUDED //============================================================================== /** An arbitrarily large integer class. A BigInteger can be used in a similar way to a normal integer, but has no size limit (except for memory and performance constraints). Negative values are possible, but the value isn't stored as 2s-complement, so be careful if you use negative values and look at the values of individual bits. */ class JUCE_API BigInteger { public: //============================================================================== /** Creates an empty BigInteger */ BigInteger(); /** Creates a BigInteger containing an integer value in its low bits. The low 32 bits of the number are initialised with this value. */ BigInteger (uint32 value); /** Creates a BigInteger containing an integer value in its low bits. The low 32 bits of the number are initialised with the absolute value passed in, and its sign is set to reflect the sign of the number. */ BigInteger (int32 value); /** Creates a BigInteger containing an integer value in its low bits. The low 64 bits of the number are initialised with the absolute value passed in, and its sign is set to reflect the sign of the number. */ BigInteger (int64 value); /** Creates a copy of another BigInteger. */ BigInteger (const BigInteger&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS BigInteger (BigInteger&&) noexcept; BigInteger& operator= (BigInteger&&) noexcept; #endif /** Destructor. */ ~BigInteger(); //============================================================================== /** Copies another BigInteger onto this one. */ BigInteger& operator= (const BigInteger&); /** Swaps the internal contents of this with another object. */ void swapWith (BigInteger&) noexcept; //============================================================================== /** Returns the value of a specified bit in the number. If the index is out-of-range, the result will be false. */ bool operator[] (int bit) const noexcept; /** Returns true if no bits are set. */ bool isZero() const noexcept; /** Returns true if the value is 1. */ bool isOne() const noexcept; /** Attempts to get the lowest 32 bits of the value as an integer. If the value is bigger than the integer limits, this will return only the lower bits. */ int toInteger() const noexcept; /** Attempts to get the lowest 64 bits of the value as an integer. If the value is bigger than the integer limits, this will return only the lower bits. */ int64 toInt64() const noexcept; //============================================================================== /** Resets the value to 0. */ void clear(); /** Clears a particular bit in the number. */ void clearBit (int bitNumber) noexcept; /** Sets a specified bit to 1. */ void setBit (int bitNumber); /** Sets or clears a specified bit. */ void setBit (int bitNumber, bool shouldBeSet); /** Sets a range of bits to be either on or off. @param startBit the first bit to change @param numBits the number of bits to change @param shouldBeSet whether to turn these bits on or off */ void setRange (int startBit, int numBits, bool shouldBeSet); /** Inserts a bit an a given position, shifting up any bits above it. */ void insertBit (int bitNumber, bool shouldBeSet); /** Returns a range of bits as a new BigInteger. e.g. getBitRangeAsInt (0, 64) would return the lowest 64 bits. @see getBitRangeAsInt */ BigInteger getBitRange (int startBit, int numBits) const; /** Returns a range of bits as an integer value. e.g. getBitRangeAsInt (0, 32) would return the lowest 32 bits. Asking for more than 32 bits isn't allowed (obviously) - for that, use getBitRange(). */ uint32 getBitRangeAsInt (int startBit, int numBits) const noexcept; /** Sets a range of bits to an integer value. Copies the given integer onto a range of bits, starting at startBit, and using up to numBits of the available bits. */ void setBitRangeAsInt (int startBit, int numBits, uint32 valueToSet); /** Shifts a section of bits left or right. @param howManyBitsLeft how far to move the bits (+ve numbers shift it left, -ve numbers shift it right). @param startBit the first bit to affect - if this is > 0, only bits above that index will be affected. */ void shiftBits (int howManyBitsLeft, int startBit); /** Returns the total number of set bits in the value. */ int countNumberOfSetBits() const noexcept; /** Looks for the index of the next set bit after a given starting point. This searches from startIndex (inclusive) upwards for the first set bit, and returns its index. If no set bits are found, it returns -1. */ int findNextSetBit (int startIndex) const noexcept; /** Looks for the index of the next clear bit after a given starting point. This searches from startIndex (inclusive) upwards for the first clear bit, and returns its index. */ int findNextClearBit (int startIndex) const noexcept; /** Returns the index of the highest set bit in the number. If the value is zero, this will return -1. */ int getHighestBit() const noexcept; //============================================================================== // All the standard arithmetic ops... BigInteger& operator+= (const BigInteger&); BigInteger& operator-= (const BigInteger&); BigInteger& operator*= (const BigInteger&); BigInteger& operator/= (const BigInteger&); BigInteger& operator|= (const BigInteger&); BigInteger& operator&= (const BigInteger&); BigInteger& operator^= (const BigInteger&); BigInteger& operator%= (const BigInteger&); BigInteger& operator<<= (int numBitsToShift); BigInteger& operator>>= (int numBitsToShift); BigInteger& operator++(); BigInteger& operator--(); BigInteger operator++ (int); BigInteger operator-- (int); BigInteger operator-() const; BigInteger operator+ (const BigInteger&) const; BigInteger operator- (const BigInteger&) const; BigInteger operator* (const BigInteger&) const; BigInteger operator/ (const BigInteger&) const; BigInteger operator| (const BigInteger&) const; BigInteger operator& (const BigInteger&) const; BigInteger operator^ (const BigInteger&) const; BigInteger operator% (const BigInteger&) const; BigInteger operator<< (int numBitsToShift) const; BigInteger operator>> (int numBitsToShift) const; bool operator== (const BigInteger&) const noexcept; bool operator!= (const BigInteger&) const noexcept; bool operator< (const BigInteger&) const noexcept; bool operator<= (const BigInteger&) const noexcept; bool operator> (const BigInteger&) const noexcept; bool operator>= (const BigInteger&) const noexcept; //============================================================================== /** Does a signed comparison of two BigIntegers. Return values are: - 0 if the numbers are the same - < 0 if this number is smaller than the other - > 0 if this number is bigger than the other */ int compare (const BigInteger& other) const noexcept; /** Compares the magnitudes of two BigIntegers, ignoring their signs. Return values are: - 0 if the numbers are the same - < 0 if this number is smaller than the other - > 0 if this number is bigger than the other */ int compareAbsolute (const BigInteger& other) const noexcept; /** Divides this value by another one and returns the remainder. This number is divided by other, leaving the quotient in this number, with the remainder being copied to the other BigInteger passed in. */ void divideBy (const BigInteger& divisor, BigInteger& remainder); /** Returns the largest value that will divide both this value and the one passed-in. */ BigInteger findGreatestCommonDivisor (BigInteger other) const; /** Performs a combined exponent and modulo operation. This BigInteger's value becomes (this ^ exponent) % modulus. */ void exponentModulo (const BigInteger& exponent, const BigInteger& modulus); /** Performs an inverse modulo on the value. i.e. the result is (this ^ -1) mod (modulus). */ void inverseModulo (const BigInteger& modulus); //============================================================================== /** Returns true if the value is less than zero. @see setNegative, negate */ bool isNegative() const noexcept; /** Changes the sign of the number to be positive or negative. @see isNegative, negate */ void setNegative (bool shouldBeNegative) noexcept; /** Inverts the sign of the number. @see isNegative, setNegative */ void negate() noexcept; //============================================================================== /** Converts the number to a string. Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). If minimumNumCharacters is greater than 0, the returned string will be padded with leading zeros to reach at least that length. */ String toString (int base, int minimumNumCharacters = 1) const; /** Reads the numeric value from a string. Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). Any invalid characters will be ignored. */ void parseString (StringRef text, int base); //============================================================================== /** Turns the number into a block of binary data. The data is arranged as little-endian, so the first byte of data is the low 8 bits of the number, and so on. @see loadFromMemoryBlock */ MemoryBlock toMemoryBlock() const; /** Converts a block of raw data into a number. The data is arranged as little-endian, so the first byte of data is the low 8 bits of the number, and so on. @see toMemoryBlock */ void loadFromMemoryBlock (const MemoryBlock& data); private: //============================================================================== HeapBlock values; size_t numValues; int highestBit; bool negative; void ensureSize (size_t); void shiftLeft (int bits, int startBit); void shiftRight (int bits, int startBit); JUCE_LEAK_DETECTOR (BigInteger) }; /** Writes a BigInteger to an OutputStream as a UTF8 decimal string. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const BigInteger& value); //============================================================================== #ifndef DOXYGEN // For backwards compatibility, BitArray is defined as an alias for BigInteger. typedef BigInteger BitArray; #endif #endif // JUCE_BIGINTEGER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp000066400000000000000000001166551320201440200305450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ class Expression::Term : public SingleThreadedReferenceCountedObject { public: Term() {} virtual ~Term() {} virtual Type getType() const noexcept = 0; virtual Term* clone() const = 0; virtual ReferenceCountedObjectPtr resolve (const Scope&, int recursionDepth) = 0; virtual String toString() const = 0; virtual double toDouble() const { return 0; } virtual int getInputIndexFor (const Term*) const { return -1; } virtual int getOperatorPrecedence() const { return 0; } virtual int getNumInputs() const { return 0; } virtual Term* getInput (int) const { return nullptr; } virtual ReferenceCountedObjectPtr negated(); virtual ReferenceCountedObjectPtr createTermToEvaluateInput (const Scope&, const Term* /*inputTerm*/, double /*overallTarget*/, Term* /*topLevelTerm*/) const { jassertfalse; return ReferenceCountedObjectPtr(); } virtual String getName() const { jassertfalse; // You shouldn't call this for an expression that's not actually a function! return String(); } virtual void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) { for (int i = getNumInputs(); --i >= 0;) getInput (i)->renameSymbol (oldSymbol, newName, scope, recursionDepth); } class SymbolVisitor { public: virtual ~SymbolVisitor() {} virtual void useSymbol (const Symbol&) = 0; }; virtual void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) { for (int i = getNumInputs(); --i >= 0;) getInput(i)->visitAllSymbols (visitor, scope, recursionDepth); } private: JUCE_DECLARE_NON_COPYABLE (Term) }; //============================================================================== struct Expression::Helpers { typedef ReferenceCountedObjectPtr TermPtr; static void checkRecursionDepth (const int depth) { if (depth > 256) throw EvaluationError ("Recursive symbol references"); } friend class Expression::Term; //============================================================================== /** An exception that can be thrown by Expression::evaluate(). */ class EvaluationError : public std::exception { public: EvaluationError (const String& desc) : description (desc) { DBG ("Expression::EvaluationError: " + description); } String description; }; //============================================================================== class Constant : public Term { public: Constant (const double val, const bool resolutionTarget) : value (val), isResolutionTarget (resolutionTarget) {} Type getType() const noexcept { return constantType; } Term* clone() const { return new Constant (value, isResolutionTarget); } TermPtr resolve (const Scope&, int) { return this; } double toDouble() const { return value; } TermPtr negated() { return new Constant (-value, isResolutionTarget); } String toString() const { String s (value); if (isResolutionTarget) s = "@" + s; return s; } double value; bool isResolutionTarget; }; //============================================================================== class BinaryTerm : public Term { public: BinaryTerm (Term* const l, Term* const r) : left (l), right (r) { jassert (l != nullptr && r != nullptr); } int getInputIndexFor (const Term* possibleInput) const { return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1); } Type getType() const noexcept { return operatorType; } int getNumInputs() const { return 2; } Term* getInput (int index) const { return index == 0 ? left.get() : (index == 1 ? right.get() : 0); } virtual double performFunction (double left, double right) const = 0; virtual void writeOperator (String& dest) const = 0; TermPtr resolve (const Scope& scope, int recursionDepth) { return new Constant (performFunction (left ->resolve (scope, recursionDepth)->toDouble(), right->resolve (scope, recursionDepth)->toDouble()), false); } String toString() const { String s; const int ourPrecendence = getOperatorPrecedence(); if (left->getOperatorPrecedence() > ourPrecendence) s << '(' << left->toString() << ')'; else s = left->toString(); writeOperator (s); if (right->getOperatorPrecedence() >= ourPrecendence) s << '(' << right->toString() << ')'; else s << right->toString(); return s; } protected: const TermPtr left, right; TermPtr createDestinationTerm (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { jassert (input == left || input == right); if (input != left && input != right) return TermPtr(); if (const Term* const dest = findDestinationFor (topLevelTerm, this)) return dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm); return new Constant (overallTarget, false); } }; //============================================================================== class SymbolTerm : public Term { public: explicit SymbolTerm (const String& sym) : symbol (sym) {} TermPtr resolve (const Scope& scope, int recursionDepth) { checkRecursionDepth (recursionDepth); return scope.getSymbolValue (symbol).term->resolve (scope, recursionDepth + 1); } Type getType() const noexcept { return symbolType; } Term* clone() const { return new SymbolTerm (symbol); } String toString() const { return symbol; } String getName() const { return symbol; } void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) { checkRecursionDepth (recursionDepth); visitor.useSymbol (Symbol (scope.getScopeUID(), symbol)); scope.getSymbolValue (symbol).term->visitAllSymbols (visitor, scope, recursionDepth + 1); } void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int /*recursionDepth*/) { if (oldSymbol.symbolName == symbol && scope.getScopeUID() == oldSymbol.scopeUID) symbol = newName; } String symbol; }; //============================================================================== class Function : public Term { public: explicit Function (const String& name) : functionName (name) {} Function (const String& name, const Array& params) : functionName (name), parameters (params) {} Type getType() const noexcept { return functionType; } Term* clone() const { return new Function (functionName, parameters); } int getNumInputs() const { return parameters.size(); } Term* getInput (int i) const { return parameters.getReference(i).term; } String getName() const { return functionName; } TermPtr resolve (const Scope& scope, int recursionDepth) { checkRecursionDepth (recursionDepth); double result = 0; const int numParams = parameters.size(); if (numParams > 0) { HeapBlock params ((size_t) numParams); for (int i = 0; i < numParams; ++i) params[i] = parameters.getReference(i).term->resolve (scope, recursionDepth + 1)->toDouble(); result = scope.evaluateFunction (functionName, params, numParams); } else { result = scope.evaluateFunction (functionName, nullptr, 0); } return new Constant (result, false); } int getInputIndexFor (const Term* possibleInput) const { for (int i = 0; i < parameters.size(); ++i) if (parameters.getReference(i).term == possibleInput) return i; return -1; } String toString() const { if (parameters.size() == 0) return functionName + "()"; String s (functionName + " ("); for (int i = 0; i < parameters.size(); ++i) { s << parameters.getReference(i).term->toString(); if (i < parameters.size() - 1) s << ", "; } s << ')'; return s; } const String functionName; Array parameters; }; //============================================================================== class DotOperator : public BinaryTerm { public: DotOperator (SymbolTerm* const l, Term* const r) : BinaryTerm (l, r) {} TermPtr resolve (const Scope& scope, int recursionDepth) { checkRecursionDepth (recursionDepth); EvaluationVisitor visitor (right, recursionDepth + 1); scope.visitRelativeScope (getSymbol()->symbol, visitor); return visitor.output; } Term* clone() const { return new DotOperator (getSymbol(), right); } String getName() const { return "."; } int getOperatorPrecedence() const { return 1; } void writeOperator (String& dest) const { dest << '.'; } double performFunction (double, double) const { return 0.0; } void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) { checkRecursionDepth (recursionDepth); visitor.useSymbol (Symbol (scope.getScopeUID(), getSymbol()->symbol)); SymbolVisitingVisitor v (right, visitor, recursionDepth + 1); try { scope.visitRelativeScope (getSymbol()->symbol, v); } catch (...) {} } void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) { checkRecursionDepth (recursionDepth); getSymbol()->renameSymbol (oldSymbol, newName, scope, recursionDepth); SymbolRenamingVisitor visitor (right, oldSymbol, newName, recursionDepth + 1); try { scope.visitRelativeScope (getSymbol()->symbol, visitor); } catch (...) {} } private: //============================================================================== class EvaluationVisitor : public Scope::Visitor { public: EvaluationVisitor (const TermPtr& t, const int recursion) : input (t), output (t), recursionCount (recursion) {} void visit (const Scope& scope) { output = input->resolve (scope, recursionCount); } const TermPtr input; TermPtr output; const int recursionCount; private: JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor) }; class SymbolVisitingVisitor : public Scope::Visitor { public: SymbolVisitingVisitor (const TermPtr& t, SymbolVisitor& v, const int recursion) : input (t), visitor (v), recursionCount (recursion) {} void visit (const Scope& scope) { input->visitAllSymbols (visitor, scope, recursionCount); } private: const TermPtr input; SymbolVisitor& visitor; const int recursionCount; JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor) }; class SymbolRenamingVisitor : public Scope::Visitor { public: SymbolRenamingVisitor (const TermPtr& t, const Expression::Symbol& symbol_, const String& newName_, const int recursionCount_) : input (t), symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {} void visit (const Scope& scope) { input->renameSymbol (symbol, newName, scope, recursionCount); } private: const TermPtr input; const Symbol& symbol; const String newName; const int recursionCount; JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor) }; SymbolTerm* getSymbol() const { return static_cast (left.get()); } JUCE_DECLARE_NON_COPYABLE (DotOperator) }; //============================================================================== class Negate : public Term { public: explicit Negate (const TermPtr& t) : input (t) { jassert (t != nullptr); } Type getType() const noexcept { return operatorType; } int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; } int getNumInputs() const { return 1; } Term* getInput (int index) const { return index == 0 ? input.get() : nullptr; } Term* clone() const { return new Negate (input->clone()); } TermPtr resolve (const Scope& scope, int recursionDepth) { return new Constant (-input->resolve (scope, recursionDepth)->toDouble(), false); } String getName() const { return "-"; } TermPtr negated() { return input; } TermPtr createTermToEvaluateInput (const Scope& scope, const Term* t, double overallTarget, Term* topLevelTerm) const { (void) t; jassert (t == input); const Term* const dest = findDestinationFor (topLevelTerm, this); return new Negate (dest == nullptr ? new Constant (overallTarget, false) : dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm)); } String toString() const { if (input->getOperatorPrecedence() > 0) return "-(" + input->toString() + ")"; return "-" + input->toString(); } private: const TermPtr input; }; //============================================================================== class Add : public BinaryTerm { public: Add (Term* const l, Term* const r) : BinaryTerm (l, r) {} Term* clone() const { return new Add (left->clone(), right->clone()); } double performFunction (double lhs, double rhs) const { return lhs + rhs; } int getOperatorPrecedence() const { return 3; } String getName() const { return "+"; } void writeOperator (String& dest) const { dest << " + "; } TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) return TermPtr(); return new Subtract (newDest, (input == left ? right : left)->clone()); } private: JUCE_DECLARE_NON_COPYABLE (Add) }; //============================================================================== class Subtract : public BinaryTerm { public: Subtract (Term* const l, Term* const r) : BinaryTerm (l, r) {} Term* clone() const { return new Subtract (left->clone(), right->clone()); } double performFunction (double lhs, double rhs) const { return lhs - rhs; } int getOperatorPrecedence() const { return 3; } String getName() const { return "-"; } void writeOperator (String& dest) const { dest << " - "; } TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) return TermPtr(); if (input == left) return new Add (newDest, right->clone()); return new Subtract (left->clone(), newDest); } private: JUCE_DECLARE_NON_COPYABLE (Subtract) }; //============================================================================== class Multiply : public BinaryTerm { public: Multiply (Term* const l, Term* const r) : BinaryTerm (l, r) {} Term* clone() const { return new Multiply (left->clone(), right->clone()); } double performFunction (double lhs, double rhs) const { return lhs * rhs; } String getName() const { return "*"; } void writeOperator (String& dest) const { dest << " * "; } int getOperatorPrecedence() const { return 2; } TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) return TermPtr(); return new Divide (newDest, (input == left ? right : left)->clone()); } private: JUCE_DECLARE_NON_COPYABLE (Multiply) }; //============================================================================== class Divide : public BinaryTerm { public: Divide (Term* const l, Term* const r) : BinaryTerm (l, r) {} Term* clone() const { return new Divide (left->clone(), right->clone()); } double performFunction (double lhs, double rhs) const { return lhs / rhs; } String getName() const { return "/"; } void writeOperator (String& dest) const { dest << " / "; } int getOperatorPrecedence() const { return 2; } TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) return TermPtr(); if (input == left) return new Multiply (newDest, right->clone()); return new Divide (left->clone(), newDest); } private: JUCE_DECLARE_NON_COPYABLE (Divide) }; //============================================================================== static Term* findDestinationFor (Term* const topLevel, const Term* const inputTerm) { const int inputIndex = topLevel->getInputIndexFor (inputTerm); if (inputIndex >= 0) return topLevel; for (int i = topLevel->getNumInputs(); --i >= 0;) { Term* const t = findDestinationFor (topLevel->getInput (i), inputTerm); if (t != nullptr) return t; } return nullptr; } static Constant* findTermToAdjust (Term* const term, const bool mustBeFlagged) { jassert (term != nullptr); if (term->getType() == constantType) { Constant* const c = static_cast (term); if (c->isResolutionTarget || ! mustBeFlagged) return c; } if (term->getType() == functionType) return nullptr; const int numIns = term->getNumInputs(); for (int i = 0; i < numIns; ++i) { Term* const input = term->getInput (i); if (input->getType() == constantType) { Constant* const c = static_cast (input); if (c->isResolutionTarget || ! mustBeFlagged) return c; } } for (int i = 0; i < numIns; ++i) { Constant* const c = findTermToAdjust (term->getInput (i), mustBeFlagged); if (c != nullptr) return c; } return nullptr; } static bool containsAnySymbols (const Term* const t) { if (t->getType() == Expression::symbolType) return true; for (int i = t->getNumInputs(); --i >= 0;) if (containsAnySymbols (t->getInput (i))) return true; return false; } //============================================================================== class SymbolCheckVisitor : public Term::SymbolVisitor { public: SymbolCheckVisitor (const Symbol& symbol_) : wasFound (false), symbol (symbol_) {} void useSymbol (const Symbol& s) { wasFound = wasFound || s == symbol; } bool wasFound; private: const Symbol& symbol; JUCE_DECLARE_NON_COPYABLE (SymbolCheckVisitor) }; //============================================================================== class SymbolListVisitor : public Term::SymbolVisitor { public: SymbolListVisitor (Array& list_) : list (list_) {} void useSymbol (const Symbol& s) { list.addIfNotAlreadyThere (s); } private: Array& list; JUCE_DECLARE_NON_COPYABLE (SymbolListVisitor) }; //============================================================================== class Parser { public: //============================================================================== Parser (String::CharPointerType& stringToParse) : text (stringToParse) { } TermPtr readUpToComma() { if (text.isEmpty()) return new Constant (0.0, false); const TermPtr e (readExpression()); if (e == nullptr || ((! readOperator (",")) && ! text.isEmpty())) throw ParseError ("Syntax error: \"" + String (text) + "\""); return e; } private: String::CharPointerType& text; //============================================================================== static inline bool isDecimalDigit (const juce_wchar c) noexcept { return c >= '0' && c <= '9'; } bool readChar (const juce_wchar required) noexcept { if (*text == required) { ++text; return true; } return false; } bool readOperator (const char* ops, char* const opType = nullptr) noexcept { text = text.findEndOfWhitespace(); while (*ops != 0) { if (readChar ((juce_wchar) (uint8) *ops)) { if (opType != nullptr) *opType = *ops; return true; } ++ops; } return false; } bool readIdentifier (String& identifier) noexcept { text = text.findEndOfWhitespace(); String::CharPointerType t (text); int numChars = 0; if (t.isLetter() || *t == '_') { ++t; ++numChars; while (t.isLetterOrDigit() || *t == '_') { ++t; ++numChars; } } if (numChars > 0) { identifier = String (text, (size_t) numChars); text = t; return true; } return false; } Term* readNumber() noexcept { text = text.findEndOfWhitespace(); String::CharPointerType t (text); const bool isResolutionTarget = (*t == '@'); if (isResolutionTarget) { ++t; t = t.findEndOfWhitespace(); text = t; } if (*t == '-') { ++t; t = t.findEndOfWhitespace(); } if (isDecimalDigit (*t) || (*t == '.' && isDecimalDigit (t[1]))) return new Constant (CharacterFunctions::readDoubleValue (text), isResolutionTarget); return nullptr; } TermPtr readExpression() { TermPtr lhs (readMultiplyOrDivideExpression()); char opType; while (lhs != nullptr && readOperator ("+-", &opType)) { TermPtr rhs (readMultiplyOrDivideExpression()); if (rhs == nullptr) throw ParseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\""); if (opType == '+') lhs = new Add (lhs, rhs); else lhs = new Subtract (lhs, rhs); } return lhs; } TermPtr readMultiplyOrDivideExpression() { TermPtr lhs (readUnaryExpression()); char opType; while (lhs != nullptr && readOperator ("*/", &opType)) { TermPtr rhs (readUnaryExpression()); if (rhs == nullptr) throw ParseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\""); if (opType == '*') lhs = new Multiply (lhs, rhs); else lhs = new Divide (lhs, rhs); } return lhs; } TermPtr readUnaryExpression() { char opType; if (readOperator ("+-", &opType)) { TermPtr e (readUnaryExpression()); if (e == nullptr) throw ParseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\""); if (opType == '-') e = e->negated(); return e; } return readPrimaryExpression(); } TermPtr readPrimaryExpression() { TermPtr e (readParenthesisedExpression()); if (e != nullptr) return e; e = readNumber(); if (e != nullptr) return e; return readSymbolOrFunction(); } TermPtr readSymbolOrFunction() { String identifier; if (readIdentifier (identifier)) { if (readOperator ("(")) // method call... { Function* const f = new Function (identifier); ScopedPointer func (f); // (can't use ScopedPointer in MSVC) TermPtr param (readExpression()); if (param == nullptr) { if (readOperator (")")) return func.release(); throw ParseError ("Expected parameters after \"" + identifier + " (\""); } f->parameters.add (Expression (param)); while (readOperator (",")) { param = readExpression(); if (param == nullptr) throw ParseError ("Expected expression after \",\""); f->parameters.add (Expression (param)); } if (readOperator (")")) return func.release(); throw ParseError ("Expected \")\""); } if (readOperator (".")) { TermPtr rhs (readSymbolOrFunction()); if (rhs == nullptr) throw ParseError ("Expected symbol or function after \".\""); if (identifier == "this") return rhs; return new DotOperator (new SymbolTerm (identifier), rhs); } // just a symbol.. jassert (identifier.trim() == identifier); return new SymbolTerm (identifier); } return TermPtr(); } TermPtr readParenthesisedExpression() { if (! readOperator ("(")) return TermPtr(); const TermPtr e (readExpression()); if (e == nullptr || ! readOperator (")")) return TermPtr(); return e; } JUCE_DECLARE_NON_COPYABLE (Parser) }; }; //============================================================================== Expression::Expression() : term (new Expression::Helpers::Constant (0, false)) { } Expression::~Expression() { } Expression::Expression (Term* const term_) : term (term_) { jassert (term != nullptr); } Expression::Expression (const double constant) : term (new Expression::Helpers::Constant (constant, false)) { } Expression::Expression (const Expression& other) : term (other.term) { } Expression& Expression::operator= (const Expression& other) { term = other.term; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Expression::Expression (Expression&& other) noexcept : term (static_cast &&> (other.term)) { } Expression& Expression::operator= (Expression&& other) noexcept { term = static_cast &&> (other.term); return *this; } #endif Expression::Expression (const String& stringToParse) { String::CharPointerType text (stringToParse.getCharPointer()); Helpers::Parser parser (text); term = parser.readUpToComma(); } Expression Expression::parse (String::CharPointerType& stringToParse) { Helpers::Parser parser (stringToParse); return Expression (parser.readUpToComma()); } double Expression::evaluate() const { return evaluate (Expression::Scope()); } double Expression::evaluate (const Expression::Scope& scope) const { try { return term->resolve (scope, 0)->toDouble(); } catch (Helpers::EvaluationError&) {} return 0; } double Expression::evaluate (const Scope& scope, String& evaluationError) const { try { return term->resolve (scope, 0)->toDouble(); } catch (Helpers::EvaluationError& e) { evaluationError = e.description; } return 0; } Expression Expression::operator+ (const Expression& other) const { return Expression (new Helpers::Add (term, other.term)); } Expression Expression::operator- (const Expression& other) const { return Expression (new Helpers::Subtract (term, other.term)); } Expression Expression::operator* (const Expression& other) const { return Expression (new Helpers::Multiply (term, other.term)); } Expression Expression::operator/ (const Expression& other) const { return Expression (new Helpers::Divide (term, other.term)); } Expression Expression::operator-() const { return Expression (term->negated()); } Expression Expression::symbol (const String& symbol) { return Expression (new Helpers::SymbolTerm (symbol)); } Expression Expression::function (const String& functionName, const Array& parameters) { return Expression (new Helpers::Function (functionName, parameters)); } Expression Expression::adjustedToGiveNewResult (const double targetValue, const Expression::Scope& scope) const { ScopedPointer newTerm (term->clone()); Helpers::Constant* termToAdjust = Helpers::findTermToAdjust (newTerm, true); if (termToAdjust == nullptr) termToAdjust = Helpers::findTermToAdjust (newTerm, false); if (termToAdjust == nullptr) { newTerm = new Helpers::Add (newTerm.release(), new Helpers::Constant (0, false)); termToAdjust = Helpers::findTermToAdjust (newTerm, false); } jassert (termToAdjust != nullptr); const Term* const parent = Helpers::findDestinationFor (newTerm, termToAdjust); if (parent == nullptr) { termToAdjust->value = targetValue; } else { const Helpers::TermPtr reverseTerm (parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm)); if (reverseTerm == nullptr) return Expression (targetValue); termToAdjust->value = reverseTerm->resolve (scope, 0)->toDouble(); } return Expression (newTerm.release()); } Expression Expression::withRenamedSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Scope& scope) const { jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); if (oldSymbol.symbolName == newName) return *this; Expression e (term->clone()); e.term->renameSymbol (oldSymbol, newName, scope, 0); return e; } bool Expression::referencesSymbol (const Expression::Symbol& symbolToCheck, const Scope& scope) const { Helpers::SymbolCheckVisitor visitor (symbolToCheck); try { term->visitAllSymbols (visitor, scope, 0); } catch (Helpers::EvaluationError&) {} return visitor.wasFound; } void Expression::findReferencedSymbols (Array& results, const Scope& scope) const { try { Helpers::SymbolListVisitor visitor (results); term->visitAllSymbols (visitor, scope, 0); } catch (Helpers::EvaluationError&) {} } String Expression::toString() const { return term->toString(); } bool Expression::usesAnySymbols() const { return Helpers::containsAnySymbols (term); } Expression::Type Expression::getType() const noexcept { return term->getType(); } String Expression::getSymbolOrFunction() const { return term->getName(); } int Expression::getNumInputs() const { return term->getNumInputs(); } Expression Expression::getInput (int index) const { return Expression (term->getInput (index)); } //============================================================================== ReferenceCountedObjectPtr Expression::Term::negated() { return new Helpers::Negate (this); } //============================================================================== Expression::ParseError::ParseError (const String& message) : description (message) { DBG ("Expression::ParseError: " + message); } //============================================================================== Expression::Symbol::Symbol (const String& scopeUID_, const String& symbolName_) : scopeUID (scopeUID_), symbolName (symbolName_) { } bool Expression::Symbol::operator== (const Symbol& other) const noexcept { return symbolName == other.symbolName && scopeUID == other.scopeUID; } bool Expression::Symbol::operator!= (const Symbol& other) const noexcept { return ! operator== (other); } //============================================================================== Expression::Scope::Scope() {} Expression::Scope::~Scope() {} Expression Expression::Scope::getSymbolValue (const String& symbol) const { if (symbol.isNotEmpty()) throw Helpers::EvaluationError ("Unknown symbol: " + symbol); return Expression(); } double Expression::Scope::evaluateFunction (const String& functionName, const double* parameters, int numParams) const { if (numParams > 0) { if (functionName == "min") { double v = parameters[0]; for (int i = 1; i < numParams; ++i) v = jmin (v, parameters[i]); return v; } if (functionName == "max") { double v = parameters[0]; for (int i = 1; i < numParams; ++i) v = jmax (v, parameters[i]); return v; } if (numParams == 1) { if (functionName == "sin") return std::sin (parameters[0]); if (functionName == "cos") return std::cos (parameters[0]); if (functionName == "tan") return std::tan (parameters[0]); if (functionName == "abs") return std::abs (parameters[0]); } } throw Helpers::EvaluationError ("Unknown function: \"" + functionName + "\""); } void Expression::Scope::visitRelativeScope (const String& scopeName, Visitor&) const { throw Helpers::EvaluationError ("Unknown symbol: " + scopeName); } String Expression::Scope::getScopeUID() const { return String(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h000066400000000000000000000262071320201440200302030ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_EXPRESSION_H_INCLUDED #define JUCE_EXPRESSION_H_INCLUDED //============================================================================== /** A class for dynamically evaluating simple numeric expressions. This class can parse a simple C-style string expression involving floating point numbers, named symbols and functions. The basic arithmetic operations of +, -, *, / are supported, as well as parentheses, and any alphanumeric identifiers are assumed to be named symbols which will be resolved when the expression is evaluated. Expressions which use identifiers and functions require a subclass of Expression::Scope to be supplied when evaluating them, and this object is expected to be able to resolve the symbol names and perform the functions that are used. */ class JUCE_API Expression { public: //============================================================================== /** Creates a simple expression with a value of 0. */ Expression(); /** Destructor. */ ~Expression(); /** Creates a simple expression with a specified constant value. */ explicit Expression (double constant); /** Creates a copy of an expression. */ Expression (const Expression&); /** Copies another expression. */ Expression& operator= (const Expression&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Expression (Expression&&) noexcept; Expression& operator= (Expression&&) noexcept; #endif /** Creates an expression by parsing a string. If there's a syntax error in the string, this will throw a ParseError exception. @throws ParseError */ explicit Expression (const String& stringToParse); /** Returns a string version of the expression. */ String toString() const; /** Returns an expression which is an addition operation of two existing expressions. */ Expression operator+ (const Expression&) const; /** Returns an expression which is a subtraction operation of two existing expressions. */ Expression operator- (const Expression&) const; /** Returns an expression which is a multiplication operation of two existing expressions. */ Expression operator* (const Expression&) const; /** Returns an expression which is a division operation of two existing expressions. */ Expression operator/ (const Expression&) const; /** Returns an expression which performs a negation operation on an existing expression. */ Expression operator-() const; /** Returns an Expression which is an identifier reference. */ static Expression symbol (const String& symbol); /** Returns an Expression which is a function call. */ static Expression function (const String& functionName, const Array& parameters); /** Returns an Expression which parses a string from a character pointer, and updates the pointer to indicate where it finished. The pointer is incremented so that on return, it indicates the character that follows the end of the expression that was parsed. If there's a syntax error in the string, this will throw a ParseError exception. @throws ParseError */ static Expression parse (String::CharPointerType& stringToParse); //============================================================================== /** When evaluating an Expression object, this class is used to resolve symbols and perform functions that the expression uses. */ class JUCE_API Scope { public: Scope(); virtual ~Scope(); /** Returns some kind of globally unique ID that identifies this scope. */ virtual String getScopeUID() const; /** Returns the value of a symbol. If the symbol is unknown, this can throw an Expression::EvaluationError exception. The member value is set to the part of the symbol that followed the dot, if there is one, e.g. for "foo.bar", symbol = "foo" and member = "bar". @throws Expression::EvaluationError */ virtual Expression getSymbolValue (const String& symbol) const; /** Executes a named function. If the function name is unknown, this can throw an Expression::EvaluationError exception. @throws Expression::EvaluationError */ virtual double evaluateFunction (const String& functionName, const double* parameters, int numParameters) const; /** Used as a callback by the Scope::visitRelativeScope() method. You should never create an instance of this class yourself, it's used by the expression evaluation code. */ class Visitor { public: virtual ~Visitor() {} virtual void visit (const Scope&) = 0; }; /** Creates a Scope object for a named scope, and then calls a visitor to do some kind of processing with this new scope. If the name is valid, this method must create a suitable (temporary) Scope object to represent it, and must call the Visitor::visit() method with this new scope. */ virtual void visitRelativeScope (const String& scopeName, Visitor& visitor) const; }; /** Evaluates this expression, without using a Scope. Without a Scope, no symbols can be used, and only basic functions such as sin, cos, tan, min, max are available. To find out about any errors during evaluation, use the other version of this method which takes a String parameter. */ double evaluate() const; /** Evaluates this expression, providing a scope that should be able to evaluate any symbols or functions that it uses. To find out about any errors during evaluation, use the other version of this method which takes a String parameter. */ double evaluate (const Scope& scope) const; /** Evaluates this expression, providing a scope that should be able to evaluate any symbols or functions that it uses. */ double evaluate (const Scope& scope, String& evaluationError) const; /** Attempts to return an expression which is a copy of this one, but with a constant adjusted to make the expression resolve to a target value. E.g. if the expression is "x + 10" and x is 5, then asking for a target value of 8 will return the expression "x + 3". Obviously some expressions can't be reversed in this way, in which case they might just be adjusted by adding a constant to the original expression. @throws Expression::EvaluationError */ Expression adjustedToGiveNewResult (double targetValue, const Scope& scope) const; /** Represents a symbol that is used in an Expression. */ struct Symbol { Symbol (const String& scopeUID, const String& symbolName); bool operator== (const Symbol&) const noexcept; bool operator!= (const Symbol&) const noexcept; String scopeUID; /**< The unique ID of the Scope that contains this symbol. */ String symbolName; /**< The name of the symbol. */ }; /** Returns a copy of this expression in which all instances of a given symbol have been renamed. */ Expression withRenamedSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope) const; /** Returns true if this expression makes use of the specified symbol. If a suitable scope is supplied, the search will dereference and recursively check all symbols, so that it can be determined whether this expression relies on the given symbol at any level in its evaluation. If the scope parameter is null, this just checks whether the expression contains any direct references to the symbol. @throws Expression::EvaluationError */ bool referencesSymbol (const Symbol& symbol, const Scope& scope) const; /** Returns true if this expression contains any symbols. */ bool usesAnySymbols() const; /** Returns a list of all symbols that may be needed to resolve this expression in the given scope. */ void findReferencedSymbols (Array& results, const Scope& scope) const; //============================================================================== /** An exception that can be thrown by Expression::parse(). */ class ParseError : public std::exception { public: ParseError (const String& message); String description; }; //============================================================================== /** Expression type. @see Expression::getType() */ enum Type { constantType, functionType, operatorType, symbolType }; /** Returns the type of this expression. */ Type getType() const noexcept; /** If this expression is a symbol, function or operator, this returns its identifier. */ String getSymbolOrFunction() const; /** Returns the number of inputs to this expression. @see getInput */ int getNumInputs() const; /** Retrieves one of the inputs to this expression. @see getNumInputs */ Expression getInput (int index) const; private: //============================================================================== class Term; struct Helpers; friend class Term; friend struct Helpers; friend struct ContainerDeletePolicy; friend class ReferenceCountedObjectPtr; ReferenceCountedObjectPtr term; explicit Expression (Term*); }; #endif // JUCE_EXPRESSION_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h000066400000000000000000000560411320201440200310100ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_MATHSFUNCTIONS_H_INCLUDED #define JUCE_MATHSFUNCTIONS_H_INCLUDED //============================================================================== /* This file sets up some handy mathematical typdefs and functions. */ //============================================================================== // Definitions for the int8, int16, int32, int64 and pointer_sized_int types. /** A platform-independent 8-bit signed integer type. */ typedef signed char int8; /** A platform-independent 8-bit unsigned integer type. */ typedef unsigned char uint8; /** A platform-independent 16-bit signed integer type. */ typedef signed short int16; /** A platform-independent 16-bit unsigned integer type. */ typedef unsigned short uint16; /** A platform-independent 32-bit signed integer type. */ typedef signed int int32; /** A platform-independent 32-bit unsigned integer type. */ typedef unsigned int uint32; #if JUCE_MSVC /** A platform-independent 64-bit integer type. */ typedef __int64 int64; /** A platform-independent 64-bit unsigned integer type. */ typedef unsigned __int64 uint64; #else /** A platform-independent 64-bit integer type. */ typedef long long int64; /** A platform-independent 64-bit unsigned integer type. */ typedef unsigned long long uint64; #endif #ifndef DOXYGEN /** A macro for creating 64-bit literals. Historically, this was needed to support portability with MSVC6, and is kept here so that old code will still compile, but nowadays every compiler will support the LL and ULL suffixes, so you should use those in preference to this macro. */ #define literal64bit(longLiteral) (longLiteral##LL) #endif #if JUCE_64BIT /** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ typedef int64 pointer_sized_int; /** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ typedef uint64 pointer_sized_uint; #elif JUCE_MSVC /** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ typedef _W64 int pointer_sized_int; /** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ typedef _W64 unsigned int pointer_sized_uint; #else /** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ typedef int pointer_sized_int; /** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ typedef unsigned int pointer_sized_uint; #endif #if JUCE_MSVC typedef pointer_sized_int ssize_t; #endif //============================================================================== // Some indispensible min/max functions /** Returns the larger of two values. */ template inline Type jmax (const Type a, const Type b) { return (a < b) ? b : a; } /** Returns the larger of three values. */ template inline Type jmax (const Type a, const Type b, const Type c) { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); } /** Returns the larger of four values. */ template inline Type jmax (const Type a, const Type b, const Type c, const Type d) { return jmax (a, jmax (b, c, d)); } /** Returns the smaller of two values. */ template inline Type jmin (const Type a, const Type b) { return (b < a) ? b : a; } /** Returns the smaller of three values. */ template inline Type jmin (const Type a, const Type b, const Type c) { return (b < a) ? ((c < b) ? c : b) : ((c < a) ? c : a); } /** Returns the smaller of four values. */ template inline Type jmin (const Type a, const Type b, const Type c, const Type d) { return jmin (a, jmin (b, c, d)); } /** Remaps a normalised value (between 0 and 1) to a target range. This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)) */ template static Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) { return targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin); } /** Remaps a value from a source range to a target range. */ template static Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax) { return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin); } /** Scans an array of values, returning the minimum value that it contains. */ template const Type findMinimum (const Type* data, int numValues) { if (numValues <= 0) return Type(); Type result (*data++); while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) { const Type& v = *data++; if (v < result) result = v; } return result; } /** Scans an array of values, returning the maximum value that it contains. */ template const Type findMaximum (const Type* values, int numValues) { if (numValues <= 0) return Type(); Type result (*values++); while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) { const Type& v = *values++; if (result < v) result = v; } return result; } /** Scans an array of values, returning the minimum and maximum values that it contains. */ template void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highest) { if (numValues <= 0) { lowest = Type(); highest = Type(); } else { Type mn (*values++); Type mx (mn); while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) { const Type& v = *values++; if (mx < v) mx = v; if (v < mn) mn = v; } lowest = mn; highest = mx; } } //============================================================================== /** Constrains a value to keep it within a given range. This will check that the specified value lies between the lower and upper bounds specified, and if not, will return the nearest value that would be in-range. Effectively, it's like calling jmax (lowerLimit, jmin (upperLimit, value)). Note that it expects that lowerLimit <= upperLimit. If this isn't true, the results will be unpredictable. @param lowerLimit the minimum value to return @param upperLimit the maximum value to return @param valueToConstrain the value to try to return @returns the closest value to valueToConstrain which lies between lowerLimit and upperLimit (inclusive) @see jlimit0To, jmin, jmax */ template inline Type jlimit (const Type lowerLimit, const Type upperLimit, const Type valueToConstrain) noexcept { jassert (lowerLimit <= upperLimit); // if these are in the wrong order, results are unpredictable.. return (valueToConstrain < lowerLimit) ? lowerLimit : ((upperLimit < valueToConstrain) ? upperLimit : valueToConstrain); } /** Returns true if a value is at least zero, and also below a specified upper limit. This is basically a quicker way to write: @code valueToTest >= 0 && valueToTest < upperLimit @endcode */ template inline bool isPositiveAndBelow (Type valueToTest, Type upperLimit) noexcept { jassert (Type() <= upperLimit); // makes no sense to call this if the upper limit is itself below zero.. return Type() <= valueToTest && valueToTest < upperLimit; } template <> inline bool isPositiveAndBelow (const int valueToTest, const int upperLimit) noexcept { jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero.. return static_cast (valueToTest) < static_cast (upperLimit); } /** Returns true if a value is at least zero, and also less than or equal to a specified upper limit. This is basically a quicker way to write: @code valueToTest >= 0 && valueToTest <= upperLimit @endcode */ template inline bool isPositiveAndNotGreaterThan (Type valueToTest, Type upperLimit) noexcept { jassert (Type() <= upperLimit); // makes no sense to call this if the upper limit is itself below zero.. return Type() <= valueToTest && valueToTest <= upperLimit; } template <> inline bool isPositiveAndNotGreaterThan (const int valueToTest, const int upperLimit) noexcept { jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero.. return static_cast (valueToTest) <= static_cast (upperLimit); } //============================================================================== /** Handy function to swap two values. */ template inline void swapVariables (Type& variable1, Type& variable2) { std::swap (variable1, variable2); } /** Handy function for avoiding unused variables warning. */ template void ignoreUnused (const Type1&) noexcept {} template void ignoreUnused (const Type1&, const Type2&) noexcept {} template void ignoreUnused (const Type1&, const Type2&, const Type3&) noexcept {} template void ignoreUnused (const Type1&, const Type2&, const Type3&, const Type4&) noexcept {} /** Handy function for getting the number of elements in a simple const C array. E.g. @code static int myArray[] = { 1, 2, 3 }; int numElements = numElementsInArray (myArray) // returns 3 @endcode */ template inline int numElementsInArray (Type (&array)[N]) { (void) array; // (required to avoid a spurious warning in MS compilers) (void) sizeof (0[array]); // This line should cause an error if you pass an object with a user-defined subscript operator return N; } //============================================================================== // Some useful maths functions that aren't always present with all compilers and build settings. /** Using juce_hypot is easier than dealing with the different types of hypot function that are provided by the various platforms and compilers. */ template inline Type juce_hypot (Type a, Type b) noexcept { #ifndef IGNORE_JUCE_HYPOT #if JUCE_MSVC return static_cast (_hypot (a, b)); #else return static_cast (hypot (a, b)); #endif #endif } #ifndef DOXYGEN template <> inline float juce_hypot (float a, float b) noexcept { #if JUCE_MSVC return (_hypotf (a, b)); #else return (hypotf (a, b)); #endif } #endif /** 64-bit abs function. */ inline int64 abs64 (const int64 n) noexcept { return (n >= 0) ? n : -n; } #if JUCE_MSVC && ! defined (DOXYGEN) // The MSVC libraries omit these functions for some reason... template Type asinh (Type x) noexcept { return std::log (x + std::sqrt (x * x + (Type) 1)); } template Type acosh (Type x) noexcept { return std::log (x + std::sqrt (x * x - (Type) 1)); } template Type atanh (Type x) noexcept { return (std::log (x + (Type) 1) - std::log (((Type) 1) - x)) / (Type) 2; } #endif //============================================================================== /** A predefined value for Pi, at double-precision. @see float_Pi */ const double double_Pi = 3.1415926535897932384626433832795; /** A predefined value for Pi, at single-precision. @see double_Pi */ const float float_Pi = 3.14159265358979323846f; /** Converts an angle in degrees to radians. */ template inline FloatType degreesToRadians (FloatType degrees) noexcept { return degrees * static_cast (double_Pi / 180.0); } /** Converts an angle in radians to degrees. */ template inline FloatType radiansToDegrees (FloatType radians) noexcept { return radians * static_cast (180.0 / double_Pi); } //============================================================================== /** The isfinite() method seems to vary between platforms, so this is a platform-independent function for it. */ template inline bool juce_isfinite (NumericType) noexcept { return true; // Integer types are always finite } template <> inline bool juce_isfinite (float value) noexcept { #if JUCE_WINDOWS && !JUCE_MINGW return _finite (value) != 0; #else return std::isfinite (value); #endif } template <> inline bool juce_isfinite (double value) noexcept { #if JUCE_WINDOWS && !JUCE_MINGW return _finite (value) != 0; #else return std::isfinite (value); #endif } //============================================================================== #if JUCE_MSVC #pragma optimize ("t", off) #ifndef __INTEL_COMPILER #pragma float_control (precise, on, push) #endif #endif /** Fast floating-point-to-integer conversion. This is faster than using the normal c++ cast to convert a float to an int, and it will round the value to the nearest integer, rather than rounding it down like the normal cast does. Note that this routine gets its speed at the expense of some accuracy, and when rounding values whose floating point component is exactly 0.5, odd numbers and even numbers will be rounded up or down differently. */ template inline int roundToInt (const FloatType value) noexcept { #ifdef __INTEL_COMPILER #pragma float_control (precise, on, push) #endif union { int asInt[2]; double asDouble; } n; n.asDouble = ((double) value) + 6755399441055744.0; #if JUCE_BIG_ENDIAN return n.asInt [1]; #else return n.asInt [0]; #endif } inline int roundToInt (int value) noexcept { return value; } #if JUCE_MSVC #ifndef __INTEL_COMPILER #pragma float_control (pop) #endif #pragma optimize ("", on) // resets optimisations to the project defaults #endif /** Fast floating-point-to-integer conversion. This is a slightly slower and slightly more accurate version of roundDoubleToInt(). It works fine for values above zero, but negative numbers are rounded the wrong way. */ inline int roundToIntAccurate (const double value) noexcept { #ifdef __INTEL_COMPILER #pragma float_control (pop) #endif return roundToInt (value + 1.5e-8); } /** Fast floating-point-to-integer conversion. This is faster than using the normal c++ cast to convert a double to an int, and it will round the value to the nearest integer, rather than rounding it down like the normal cast does. Note that this routine gets its speed at the expense of some accuracy, and when rounding values whose floating point component is exactly 0.5, odd numbers and even numbers will be rounded up or down differently. For a more accurate conversion, see roundDoubleToIntAccurate(). */ inline int roundDoubleToInt (const double value) noexcept { return roundToInt (value); } /** Fast floating-point-to-integer conversion. This is faster than using the normal c++ cast to convert a float to an int, and it will round the value to the nearest integer, rather than rounding it down like the normal cast does. Note that this routine gets its speed at the expense of some accuracy, and when rounding values whose floating point component is exactly 0.5, odd numbers and even numbers will be rounded up or down differently. */ inline int roundFloatToInt (const float value) noexcept { return roundToInt (value); } //============================================================================== /** Returns true if the specified integer is a power-of-two. */ template bool isPowerOfTwo (IntegerType value) { return (value & (value - 1)) == 0; } /** Returns the smallest power-of-two which is equal to or greater than the given integer. */ inline int nextPowerOfTwo (int n) noexcept { --n; n |= (n >> 1); n |= (n >> 2); n |= (n >> 4); n |= (n >> 8); n |= (n >> 16); return n + 1; } /** Returns the number of bits in a 32-bit integer. */ inline int countNumberOfBits (uint32 n) noexcept { n -= ((n >> 1) & 0x55555555); n = (((n >> 2) & 0x33333333) + (n & 0x33333333)); n = (((n >> 4) + n) & 0x0f0f0f0f); n += (n >> 8); n += (n >> 16); return (int) (n & 0x3f); } /** Returns the number of bits in a 64-bit integer. */ inline int countNumberOfBits (uint64 n) noexcept { return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32)); } /** Performs a modulo operation, but can cope with the dividend being negative. The divisor must be greater than zero. */ template IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept { jassert (divisor > 0); dividend %= divisor; return (dividend < 0) ? (dividend + divisor) : dividend; } /** Returns the square of its argument. */ template NumericType square (NumericType n) noexcept { return n * n; } //============================================================================== #if JUCE_INTEL || defined (DOXYGEN) /** This macro can be applied to a float variable to check whether it contains a denormalised value, and to normalise it if necessary. On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. */ #define JUCE_UNDENORMALISE(x) { (x) += 0.1f; (x) -= 0.1f; } #else #define JUCE_UNDENORMALISE(x) #endif //============================================================================== /** This namespace contains a few template classes for helping work out class type variations. */ namespace TypeHelpers { #if JUCE_VC8_OR_EARLIER #define PARAMETER_TYPE(type) const type& #else /** The ParameterType struct is used to find the best type to use when passing some kind of object as a parameter. Of course, this is only likely to be useful in certain esoteric template situations. Because "typename TypeHelpers::ParameterType::type" is a bit of a mouthful, there's a PARAMETER_TYPE(SomeClass) macro that you can use to get the same effect. E.g. "myFunction (PARAMETER_TYPE (int), PARAMETER_TYPE (MyObject))" would evaluate to "myfunction (int, const MyObject&)", keeping any primitive types as pass-by-value, but passing objects as a const reference, to avoid copying. */ template struct ParameterType { typedef const Type& type; }; #if ! DOXYGEN template struct ParameterType { typedef Type& type; }; template struct ParameterType { typedef Type* type; }; template <> struct ParameterType { typedef char type; }; template <> struct ParameterType { typedef unsigned char type; }; template <> struct ParameterType { typedef short type; }; template <> struct ParameterType { typedef unsigned short type; }; template <> struct ParameterType { typedef int type; }; template <> struct ParameterType { typedef unsigned int type; }; template <> struct ParameterType { typedef long type; }; template <> struct ParameterType { typedef unsigned long type; }; template <> struct ParameterType { typedef int64 type; }; template <> struct ParameterType { typedef uint64 type; }; template <> struct ParameterType { typedef bool type; }; template <> struct ParameterType { typedef float type; }; template <> struct ParameterType { typedef double type; }; #endif /** A helpful macro to simplify the use of the ParameterType template. @see ParameterType */ #define PARAMETER_TYPE(a) typename TypeHelpers::ParameterType::type #endif /** These templates are designed to take a type, and if it's a double, they return a double type; for anything else, they return a float type. */ template struct SmallestFloatType { typedef float type; }; template <> struct SmallestFloatType { typedef double type; }; } //============================================================================== #endif // JUCE_MATHSFUNCTIONS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h000066400000000000000000000135041320201440200314250ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_NORMALISABLERANGE_H_INCLUDED #define JUCE_NORMALISABLERANGE_H_INCLUDED //============================================================================== /** Represents a mapping between an arbitrary range of values and a normalised 0->1 range. The properties of the mapping also include an optional snapping interval and skew-factor. @see Range */ template class NormalisableRange { public: /** Creates a continuous range that performs a dummy mapping. */ NormalisableRange() noexcept : start(), end (1), interval(), skew (static_cast (1)) {} /** Creates a copy of another range. */ NormalisableRange (const NormalisableRange& other) noexcept : start (other.start), end (other.end), interval (other.interval), skew (other.skew) { checkInvariants(); } /** Creates a copy of another range. */ NormalisableRange& operator= (const NormalisableRange& other) noexcept { start = other.start; end = other.end; interval = other.interval; skew = other.skew; checkInvariants(); return *this; } /** Creates a NormalisableRange with a given range, interval and skew factor. */ NormalisableRange (ValueType rangeStart, ValueType rangeEnd, ValueType intervalValue, ValueType skewFactor) noexcept : start (rangeStart), end (rangeEnd), interval (intervalValue), skew (skewFactor) { checkInvariants(); } /** Creates a NormalisableRange with a given range and interval, but a dummy skew-factor. */ NormalisableRange (ValueType rangeStart, ValueType rangeEnd, ValueType intervalValue) noexcept : start (rangeStart), end (rangeEnd), interval (intervalValue), skew (static_cast (1)) { checkInvariants(); } /** Creates a NormalisableRange with a given range, continuous interval, but a dummy skew-factor. */ NormalisableRange (ValueType rangeStart, ValueType rangeEnd) noexcept : start (rangeStart), end (rangeEnd), interval(), skew (static_cast (1)) { checkInvariants(); } /** Uses the properties of this mapping to convert a non-normalised value to its 0->1 representation. */ ValueType convertTo0to1 (ValueType v) const noexcept { ValueType proportion = (v - start) / (end - start); if (skew != static_cast (1)) proportion = std::pow (proportion, skew); return proportion; } /** Uses the properties of this mapping to convert a normalised 0->1 value to its full-range representation. */ ValueType convertFrom0to1 (ValueType proportion) const noexcept { if (skew != static_cast (1) && proportion > ValueType()) proportion = std::exp (std::log (proportion) / skew); return start + (end - start) * proportion; } /** Takes a non-normalised value and snaps it based on the interval property of this NormalisedRange. */ ValueType snapToLegalValue (ValueType v) const noexcept { if (interval > ValueType()) v = start + interval * std::floor ((v - start) / interval + static_cast (0.5)); if (v <= start || end <= start) return start; if (v >= end) return end; return v; } /** The start of the non-normalised range. */ ValueType start; /** The end of the non-normalised range. */ ValueType end; /** The snapping interval that should be used (in non-normalised value). Use 0 for a continuous range. */ ValueType interval; /** An optional skew factor that alters the way values are distribute across the range. The skew factor lets you skew the mapping logarithmically so that larger or smaller values are given a larger proportion of the avilable space. A factor of 1.0 has no skewing effect at all. If the factor is < 1.0, the lower end of the range will fill more of the slider's length; if the factor is > 1.0, the upper end of the range will be expanded. */ ValueType skew; private: void checkInvariants() const { jassert (end > start); jassert (interval >= ValueType()); jassert (skew > ValueType()); } }; #endif // JUCE_NORMALISABLERANGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp000066400000000000000000000122131320201440200276070ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ Random::Random (const int64 seedValue) noexcept : seed (seedValue) { } Random::Random() : seed (1) { setSeedRandomly(); } Random::~Random() noexcept { } void Random::setSeed (const int64 newSeed) noexcept { seed = newSeed; } void Random::combineSeed (const int64 seedValue) noexcept { seed ^= nextInt64() ^ seedValue; } void Random::setSeedRandomly() { static int64 globalSeed = 0; combineSeed (globalSeed ^ (int64) (pointer_sized_int) this); combineSeed (Time::getMillisecondCounter()); combineSeed (Time::getHighResolutionTicks()); combineSeed (Time::getHighResolutionTicksPerSecond()); combineSeed (Time::currentTimeMillis()); globalSeed ^= seed; } Random& Random::getSystemRandom() noexcept { static Random sysRand; return sysRand; } //============================================================================== int Random::nextInt() noexcept { seed = (seed * 0x5deece66dLL + 11) & 0xffffffffffffLL; return (int) (seed >> 16); } int Random::nextInt (const int maxValue) noexcept { jassert (maxValue > 0); return (int) ((((unsigned int) nextInt()) * (uint64) maxValue) >> 32); } int Random::nextInt (Range range) noexcept { return range.getStart() + nextInt (range.getLength()); } int64 Random::nextInt64() noexcept { return (((int64) nextInt()) << 32) | (int64) (uint64) (uint32) nextInt(); } bool Random::nextBool() noexcept { return (nextInt() & 0x40000000) != 0; } float Random::nextFloat() noexcept { return static_cast (nextInt()) / (std::numeric_limits::max() + 1.0f); } double Random::nextDouble() noexcept { return static_cast (nextInt()) / (std::numeric_limits::max() + 1.0); } BigInteger Random::nextLargeNumber (const BigInteger& maximumValue) { BigInteger n; do { fillBitsRandomly (n, 0, maximumValue.getHighestBit() + 1); } while (n >= maximumValue); return n; } void Random::fillBitsRandomly (void* const buffer, size_t bytes) { int* d = static_cast (buffer); for (; bytes >= sizeof (int); bytes -= sizeof (int)) *d++ = nextInt(); if (bytes > 0) { const int lastBytes = nextInt(); memcpy (d, &lastBytes, bytes); } } void Random::fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits) { arrayToChange.setBit (startBit + numBits - 1, true); // to force the array to pre-allocate space while ((startBit & 31) != 0 && numBits > 0) { arrayToChange.setBit (startBit++, nextBool()); --numBits; } while (numBits >= 32) { arrayToChange.setBitRangeAsInt (startBit, 32, (unsigned int) nextInt()); startBit += 32; numBits -= 32; } while (--numBits >= 0) arrayToChange.setBit (startBit + numBits, nextBool()); } //============================================================================== #if JUCE_UNIT_TESTS class RandomTests : public UnitTest { public: RandomTests() : UnitTest ("Random") {} void runTest() { beginTest ("Random"); Random r = getRandom(); for (int i = 2000; --i >= 0;) { expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); expect (r.nextInt (1) == 0); int n = r.nextInt (50) + 1; expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); n = r.nextInt (0x7ffffffe) + 1; expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); } } }; static RandomTests randomTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_Random.h000066400000000000000000000126721320201440200272650ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_RANDOM_H_INCLUDED #define JUCE_RANDOM_H_INCLUDED //============================================================================== /** A random number generator. You can create a Random object and use it to generate a sequence of random numbers. */ class JUCE_API Random { public: //============================================================================== /** Creates a Random object based on a seed value. For a given seed value, the subsequent numbers generated by this object will be predictable, so a good idea is to set this value based on the time, e.g. new Random (Time::currentTimeMillis()) */ explicit Random (int64 seedValue) noexcept; /** Creates a Random object using a random seed value. Internally, this calls setSeedRandomly() to randomise the seed. */ Random(); /** Destructor. */ ~Random() noexcept; /** Returns the next random 32 bit integer. @returns a random integer from the full range 0x80000000 to 0x7fffffff */ int nextInt() noexcept; /** Returns the next random number, limited to a given range. The maxValue parameter may not be negative, or zero. @returns a random integer between 0 (inclusive) and maxValue (exclusive). */ int nextInt (int maxValue) noexcept; /** Returns the next random number, limited to a given range. @returns a random integer between the range start (inclusive) and its end (exclusive). */ int nextInt (Range range) noexcept; /** Returns the next 64-bit random number. @returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff */ int64 nextInt64() noexcept; /** Returns the next random floating-point number. @returns a random value in the range 0 to 1.0 */ float nextFloat() noexcept; /** Returns the next random floating-point number. @returns a random value in the range 0 to 1.0 */ double nextDouble() noexcept; /** Returns the next random boolean value. */ bool nextBool() noexcept; /** Returns a BigInteger containing a random number. @returns a random value in the range 0 to (maximumValue - 1). */ BigInteger nextLargeNumber (const BigInteger& maximumValue); /** Fills a block of memory with random values. */ void fillBitsRandomly (void* bufferToFill, size_t sizeInBytes); /** Sets a range of bits in a BigInteger to random values. */ void fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits); //============================================================================== /** Resets this Random object to a given seed value. */ void setSeed (int64 newSeed) noexcept; /** Returns the RNG's current seed. */ int64 getSeed() const noexcept { return seed; } /** Merges this object's seed with another value. This sets the seed to be a value created by combining the current seed and this new value. */ void combineSeed (int64 seedValue) noexcept; /** Reseeds this generator using a value generated from various semi-random system properties like the current time, etc. Because this function convolves the time with the last seed value, calling it repeatedly will increase the randomness of the final result. */ void setSeedRandomly(); /** The overhead of creating a new Random object is fairly small, but if you want to avoid it, you can call this method to get a global shared Random object. It's not thread-safe though, so threads should use their own Random object, otherwise you run the risk of your random numbers becoming.. erm.. randomly corrupted.. */ static Random& getSystemRandom() noexcept; private: //============================================================================== int64 seed; JUCE_LEAK_DETECTOR (Random) }; #endif // JUCE_RANDOM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/maths/juce_Range.h000066400000000000000000000265511320201440200271020ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_RANGE_H_INCLUDED #define JUCE_RANGE_H_INCLUDED //============================================================================== /** A general-purpose range object, that simply represents any linear range with a start and end point. Note that when checking whether values fall within the range, the start value is considered to be inclusive, and the end of the range exclusive. The templated parameter is expected to be a primitive integer or floating point type, though class types could also be used if they behave in a number-like way. */ template class Range { public: //============================================================================== /** Constructs an empty range. */ Range() noexcept : start(), end() { } /** Constructs a range with given start and end values. */ Range (const ValueType startValue, const ValueType endValue) noexcept : start (startValue), end (jmax (startValue, endValue)) { } /** Constructs a copy of another range. */ Range (const Range& other) noexcept : start (other.start), end (other.end) { } /** Copies another range object. */ Range& operator= (Range other) noexcept { start = other.start; end = other.end; return *this; } /** Returns the range that lies between two positions (in either order). */ static Range between (const ValueType position1, const ValueType position2) noexcept { return position1 < position2 ? Range (position1, position2) : Range (position2, position1); } /** Returns a range with a given start and length. */ static Range withStartAndLength (const ValueType startValue, const ValueType length) noexcept { jassert (length >= ValueType()); return Range (startValue, startValue + length); } /** Returns a range with the specified start position and a length of zero. */ static Range emptyRange (const ValueType start) noexcept { return Range (start, start); } //============================================================================== /** Returns the start of the range. */ inline ValueType getStart() const noexcept { return start; } /** Returns the length of the range. */ inline ValueType getLength() const noexcept { return end - start; } /** Returns the end of the range. */ inline ValueType getEnd() const noexcept { return end; } /** Returns true if the range has a length of zero. */ inline bool isEmpty() const noexcept { return start == end; } //============================================================================== /** Changes the start position of the range, leaving the end position unchanged. If the new start position is higher than the current end of the range, the end point will be pushed along to equal it, leaving an empty range at the new position. */ void setStart (const ValueType newStart) noexcept { start = newStart; if (end < newStart) end = newStart; } /** Returns a range with the same end as this one, but a different start. If the new start position is higher than the current end of the range, the end point will be pushed along to equal it, returning an empty range at the new position. */ Range withStart (const ValueType newStart) const noexcept { return Range (newStart, jmax (newStart, end)); } /** Returns a range with the same length as this one, but moved to have the given start position. */ Range movedToStartAt (const ValueType newStart) const noexcept { return Range (newStart, end + (newStart - start)); } /** Changes the end position of the range, leaving the start unchanged. If the new end position is below the current start of the range, the start point will be pushed back to equal the new end point. */ void setEnd (const ValueType newEnd) noexcept { end = newEnd; if (newEnd < start) start = newEnd; } /** Returns a range with the same start position as this one, but a different end. If the new end position is below the current start of the range, the start point will be pushed back to equal the new end point. */ Range withEnd (const ValueType newEnd) const noexcept { return Range (jmin (start, newEnd), newEnd); } /** Returns a range with the same length as this one, but moved to have the given end position. */ Range movedToEndAt (const ValueType newEnd) const noexcept { return Range (start + (newEnd - end), newEnd); } /** Changes the length of the range. Lengths less than zero are treated as zero. */ void setLength (const ValueType newLength) noexcept { end = start + jmax (ValueType(), newLength); } /** Returns a range with the same start as this one, but a different length. Lengths less than zero are treated as zero. */ Range withLength (const ValueType newLength) const noexcept { return Range (start, start + newLength); } //============================================================================== /** Adds an amount to the start and end of the range. */ inline Range operator+= (const ValueType amountToAdd) noexcept { start += amountToAdd; end += amountToAdd; return *this; } /** Subtracts an amount from the start and end of the range. */ inline Range operator-= (const ValueType amountToSubtract) noexcept { start -= amountToSubtract; end -= amountToSubtract; return *this; } /** Returns a range that is equal to this one with an amount added to its start and end. */ Range operator+ (const ValueType amountToAdd) const noexcept { return Range (start + amountToAdd, end + amountToAdd); } /** Returns a range that is equal to this one with the specified amount subtracted from its start and end. */ Range operator- (const ValueType amountToSubtract) const noexcept { return Range (start - amountToSubtract, end - amountToSubtract); } bool operator== (Range other) const noexcept { return start == other.start && end == other.end; } bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; } //============================================================================== /** Returns true if the given position lies inside this range. */ bool contains (const ValueType position) const noexcept { return start <= position && position < end; } /** Returns the nearest value to the one supplied, which lies within the range. */ ValueType clipValue (const ValueType value) const noexcept { return jlimit (start, end, value); } /** Returns true if the given range lies entirely inside this range. When making this comparison, the start value is considered to be inclusive, and the end of the range exclusive. */ bool contains (Range other) const noexcept { return start <= other.start && end >= other.end; } /** Returns true if the given range intersects this one. */ bool intersects (Range other) const noexcept { return other.start < end && start < other.end; } /** Returns the range that is the intersection of the two ranges, or an empty range with an undefined start position if they don't overlap. */ Range getIntersectionWith (Range other) const noexcept { return Range (jmax (start, other.start), jmin (end, other.end)); } /** Returns the smallest range that contains both this one and the other one. */ Range getUnionWith (Range other) const noexcept { return Range (jmin (start, other.start), jmax (end, other.end)); } /** Returns the smallest range that contains both this one and the given value. */ Range getUnionWith (const ValueType valueToInclude) const noexcept { return Range (jmin (valueToInclude, start), jmax (valueToInclude, end)); } /** Returns a given range, after moving it forwards or backwards to fit it within this range. If the supplied range has a greater length than this one, the return value will be this range. Otherwise, if the supplied range is smaller than this one, the return value will be the new range, shifted forwards or backwards so that it doesn't extend beyond this one, but keeping its original length. */ Range constrainRange (Range rangeToConstrain) const noexcept { const ValueType otherLen = rangeToConstrain.getLength(); return getLength() <= otherLen ? *this : rangeToConstrain.movedToStartAt (jlimit (start, end - otherLen, rangeToConstrain.getStart())); } /** Scans an array of values for its min and max, and returns these as a Range. */ static Range findMinAndMax (const ValueType* values, int numValues) noexcept { if (numValues <= 0) return Range(); const ValueType first (*values++); Range r (first, first); while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) { const ValueType v (*values++); if (r.end < v) r.end = v; if (v < r.start) r.start = v; } return r; } private: //============================================================================== ValueType start, end; }; #endif // JUCE_RANGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/000077500000000000000000000000001320201440200250525ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h000066400000000000000000000437341320201440200274600ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_ATOMIC_H_INCLUDED #define JUCE_ATOMIC_H_INCLUDED //============================================================================== /** Simple class to hold a primitive value and perform atomic operations on it. The type used must be a 32 or 64 bit primitive, like an int, pointer, etc. There are methods to perform most of the basic atomic operations. */ template class Atomic { public: /** Creates a new value, initialised to zero. */ inline Atomic() noexcept : value (0) { } /** Creates a new value, with a given initial value. */ inline explicit Atomic (const Type initialValue) noexcept : value (initialValue) { } /** Copies another value (atomically). */ inline Atomic (const Atomic& other) noexcept : value (other.get()) { } /** Destructor. */ inline ~Atomic() noexcept { // This class can only be used for types which are 32 or 64 bits in size. static_jassert (sizeof (Type) == 4 || sizeof (Type) == 8); } /** Atomically reads and returns the current value. */ Type get() const noexcept; /** Copies another value onto this one (atomically). */ inline Atomic& operator= (const Atomic& other) noexcept { exchange (other.get()); return *this; } /** Copies another value onto this one (atomically). */ inline Atomic& operator= (const Type newValue) noexcept { exchange (newValue); return *this; } /** Atomically sets the current value. */ void set (Type newValue) noexcept { exchange (newValue); } /** Atomically sets the current value, returning the value that was replaced. */ Type exchange (Type value) noexcept; /** Atomically adds a number to this value, returning the new value. */ Type operator+= (Type amountToAdd) noexcept; /** Atomically subtracts a number from this value, returning the new value. */ Type operator-= (Type amountToSubtract) noexcept; /** Atomically increments this value, returning the new value. */ Type operator++() noexcept; /** Atomically decrements this value, returning the new value. */ Type operator--() noexcept; /** Atomically compares this value with a target value, and if it is equal, sets this to be equal to a new value. This operation is the atomic equivalent of doing this: @code bool compareAndSetBool (Type newValue, Type valueToCompare) { if (get() == valueToCompare) { set (newValue); return true; } return false; } @endcode @returns true if the comparison was true and the value was replaced; false if the comparison failed and the value was left unchanged. @see compareAndSetValue */ bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept; /** Atomically compares this value with a target value, and if it is equal, sets this to be equal to a new value. This operation is the atomic equivalent of doing this: @code Type compareAndSetValue (Type newValue, Type valueToCompare) { Type oldValue = get(); if (oldValue == valueToCompare) set (newValue); return oldValue; } @endcode @returns the old value before it was changed. @see compareAndSetBool */ Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept; /** Implements a memory read/write barrier. */ static void memoryBarrier() noexcept; //============================================================================== #if JUCE_64BIT JUCE_ALIGN (8) #else JUCE_ALIGN (4) #endif /** The raw value that this class operates on. This is exposed publically in case you need to manipulate it directly for performance reasons. */ volatile Type value; private: template static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; } static inline Type castFrom32Bit (int32 value) noexcept { return castTo (value); } static inline Type castFrom64Bit (int64 value) noexcept { return castTo (value); } static inline int32 castTo32Bit (Type value) noexcept { return castTo (value); } static inline int64 castTo64Bit (Type value) noexcept { return castTo (value); } Type operator++ (int); // better to just use pre-increment with atomics.. Type operator-- (int); /** This templated negate function will negate pointers as well as integers */ template inline ValueType negateValue (ValueType n) noexcept { return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n : (sizeof (ValueType) == 2 ? (ValueType) -(short) n : (sizeof (ValueType) == 4 ? (ValueType) -(int) n : ((ValueType) -(int64) n))); } /** This templated negate function will negate pointers as well as integers */ template inline PointerType* negateValue (PointerType* n) noexcept { return reinterpret_cast (-reinterpret_cast (n)); } }; //============================================================================== /* The following code is in the header so that the atomics can be inlined where possible... */ #if JUCE_MAC && (JUCE_PPC || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)) #define JUCE_ATOMICS_MAC_LEGACY 1 // Older OSX builds using gcc4.1 or earlier #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 #define JUCE_MAC_ATOMICS_VOLATILE #else #define JUCE_MAC_ATOMICS_VOLATILE volatile #endif #if JUCE_PPC // None of these atomics are available for PPC or for iOS 3.1 or earlier!! template static Type OSAtomicAdd64Barrier (Type b, JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return *a += b; } template static Type OSAtomicIncrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return ++*a; } template static Type OSAtomicDecrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return --*a; } template static bool OSAtomicCompareAndSwap64Barrier (Type old, Type newValue, JUCE_MAC_ATOMICS_VOLATILE Type* value) noexcept { jassertfalse; if (old == *value) { *value = newValue; return true; } return false; } #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 #endif //============================================================================== #elif JUCE_GCC || JUCE_CLANG #define JUCE_ATOMICS_GCC 1 // GCC with intrinsics #if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes) #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 #endif //============================================================================== #else #define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics #if JUCE_USE_MSVC_INTRINSICS #ifndef __INTEL_COMPILER #pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \ _InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier) #endif #define juce_InterlockedExchange(a, b) _InterlockedExchange(a, b) #define juce_InterlockedIncrement(a) _InterlockedIncrement(a) #define juce_InterlockedDecrement(a) _InterlockedDecrement(a) #define juce_InterlockedExchangeAdd(a, b) _InterlockedExchangeAdd(a, b) #define juce_InterlockedCompareExchange(a, b, c) _InterlockedCompareExchange(a, b, c) #define juce_InterlockedCompareExchange64(a, b, c) _InterlockedCompareExchange64(a, b, c) #define juce_MemoryBarrier _ReadWriteBarrier #else long juce_InterlockedExchange (volatile long* a, long b) noexcept; long juce_InterlockedIncrement (volatile long* a) noexcept; long juce_InterlockedDecrement (volatile long* a) noexcept; long juce_InterlockedExchangeAdd (volatile long* a, long b) noexcept; long juce_InterlockedCompareExchange (volatile long* a, long b, long c) noexcept; __int64 juce_InterlockedCompareExchange64 (volatile __int64* a, __int64 b, __int64 c) noexcept; inline void juce_MemoryBarrier() noexcept { long x = 0; juce_InterlockedIncrement (&x); } #endif #if JUCE_64BIT #ifndef __INTEL_COMPILER #pragma intrinsic (_InterlockedExchangeAdd64, _InterlockedExchange64, _InterlockedIncrement64, _InterlockedDecrement64) #endif #define juce_InterlockedExchangeAdd64(a, b) _InterlockedExchangeAdd64(a, b) #define juce_InterlockedExchange64(a, b) _InterlockedExchange64(a, b) #define juce_InterlockedIncrement64(a) _InterlockedIncrement64(a) #define juce_InterlockedDecrement64(a) _InterlockedDecrement64(a) #else // None of these atomics are available in a 32-bit Windows build!! template static Type juce_InterlockedExchangeAdd64 (volatile Type* a, Type b) noexcept { jassertfalse; Type old = *a; *a += b; return old; } template static Type juce_InterlockedExchange64 (volatile Type* a, Type b) noexcept { jassertfalse; Type old = *a; *a = b; return old; } template static Type juce_InterlockedIncrement64 (volatile Type* a) noexcept { jassertfalse; return ++*a; } template static Type juce_InterlockedDecrement64 (volatile Type* a) noexcept { jassertfalse; return --*a; } #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 #endif #endif #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4311) // (truncation warning) #endif //============================================================================== template inline Type Atomic::get() const noexcept { #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value)) : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value)); #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchangeAdd ((volatile long*) &value, (long) 0)) : castFrom64Bit ((int64) juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) 0)); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) : castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); #endif } template inline Type Atomic::exchange (const Type newValue) noexcept { #if JUCE_ATOMICS_MAC_LEGACY || JUCE_ATOMICS_GCC Type currentVal = value; while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } return currentVal; #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchange ((volatile long*) &value, (long) castTo32Bit (newValue))) : castFrom64Bit ((int64) juce_InterlockedExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue))); #endif } template inline Type Atomic::operator+= (const Type amountToAdd) noexcept { #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amountToAdd), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : (Type) OSAtomicAdd64Barrier ((int64_t) amountToAdd, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) (juce_InterlockedExchangeAdd ((volatile long*) &value, (long) amountToAdd) + (long) amountToAdd) : (Type) (juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) amountToAdd) + (__int64) amountToAdd); #elif JUCE_ATOMICS_GCC return (Type) __sync_add_and_fetch (&value, amountToAdd); #endif } template inline Type Atomic::operator-= (const Type amountToSubtract) noexcept { return operator+= (negateValue (amountToSubtract)); } template inline Type Atomic::operator++() noexcept { #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : (Type) OSAtomicIncrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedIncrement ((volatile long*) &value) : (Type) juce_InterlockedIncrement64 ((volatile __int64*) &value); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1) : (Type) __sync_add_and_fetch ((int64_t*) &value, 1); #endif } template inline Type Atomic::operator--() noexcept { #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : (Type) OSAtomicDecrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedDecrement ((volatile long*) &value) : (Type) juce_InterlockedDecrement64 ((volatile __int64*) &value); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1) : (Type) __sync_add_and_fetch ((int64_t*) &value, -1); #endif } template inline bool Atomic::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept { #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return compareAndSetValue (newValue, valueToCompare) == valueToCompare; #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)); #endif } template inline Type Atomic::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept { #if JUCE_ATOMICS_MAC_LEGACY for (;;) // Annoying workaround for only having a bool CAS operation.. { if (compareAndSetBool (newValue, valueToCompare)) return valueToCompare; const Type result = value; if (result != valueToCompare) return result; } #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedCompareExchange ((volatile long*) &value, (long) castTo32Bit (newValue), (long) castTo32Bit (valueToCompare))) : castFrom64Bit ((int64) juce_InterlockedCompareExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue), (__int64) castTo64Bit (valueToCompare))); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); #endif } template inline void Atomic::memoryBarrier() noexcept { #if JUCE_ATOMICS_MAC_LEGACY OSMemoryBarrier(); #elif JUCE_ATOMICS_GCC __sync_synchronize(); #elif JUCE_ATOMICS_WINDOWS juce_MemoryBarrier(); #endif } #if JUCE_MSVC #pragma warning (pop) #endif #endif // JUCE_ATOMIC_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h000066400000000000000000000253271320201440200301410ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_BYTEORDER_H_INCLUDED #define JUCE_BYTEORDER_H_INCLUDED //============================================================================== /** Contains static methods for converting the byte order between different endiannesses. */ class JUCE_API ByteOrder { public: //============================================================================== /** Swaps the upper and lower bytes of a 16-bit integer. */ static uint16 swap (uint16 value) noexcept; /** Reverses the order of the 4 bytes in a 32-bit integer. */ static uint32 swap (uint32 value) noexcept; /** Reverses the order of the 8 bytes in a 64-bit integer. */ static uint64 swap (uint64 value) noexcept; //============================================================================== /** Swaps the byte order of a 16-bit int if the CPU is big-endian */ static uint16 swapIfBigEndian (uint16 value) noexcept; /** Swaps the byte order of a 32-bit int if the CPU is big-endian */ static uint32 swapIfBigEndian (uint32 value) noexcept; /** Swaps the byte order of a 64-bit int if the CPU is big-endian */ static uint64 swapIfBigEndian (uint64 value) noexcept; /** Swaps the byte order of a 16-bit int if the CPU is little-endian */ static uint16 swapIfLittleEndian (uint16 value) noexcept; /** Swaps the byte order of a 32-bit int if the CPU is little-endian */ static uint32 swapIfLittleEndian (uint32 value) noexcept; /** Swaps the byte order of a 64-bit int if the CPU is little-endian */ static uint64 swapIfLittleEndian (uint64 value) noexcept; //============================================================================== /** Turns 4 bytes into a little-endian integer. */ static uint32 littleEndianInt (const void* bytes) noexcept; /** Turns 8 bytes into a little-endian integer. */ static uint64 littleEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a little-endian integer. */ static uint16 littleEndianShort (const void* bytes) noexcept; /** Turns 4 bytes into a big-endian integer. */ static uint32 bigEndianInt (const void* bytes) noexcept; /** Turns 8 bytes into a big-endian integer. */ static uint64 bigEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a big-endian integer. */ static uint16 bigEndianShort (const void* bytes) noexcept; //============================================================================== /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ static int littleEndian24Bit (const void* bytes) noexcept; /** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ static int bigEndian24Bit (const void* bytes) noexcept; /** Copies a 24-bit number to 3 little-endian bytes. */ static void littleEndian24BitToChars (int value, void* destBytes) noexcept; /** Copies a 24-bit number to 3 big-endian bytes. */ static void bigEndian24BitToChars (int value, void* destBytes) noexcept; //============================================================================== /** Returns true if the current CPU is big-endian. */ static bool isBigEndian() noexcept; private: ByteOrder() JUCE_DELETED_FUNCTION; JUCE_DECLARE_NON_COPYABLE (ByteOrder) }; //============================================================================== #if JUCE_USE_MSVC_INTRINSICS && ! defined (__INTEL_COMPILER) #pragma intrinsic (_byteswap_ulong) #endif inline uint16 ByteOrder::swap (uint16 n) noexcept { #if JUCE_USE_MSVC_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! return static_cast (_byteswap_ushort (n)); #else return static_cast ((n << 8) | (n >> 8)); #endif } inline uint32 ByteOrder::swap (uint32 n) noexcept { #if JUCE_MAC || JUCE_IOS return OSSwapInt32 (n); #elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM asm("bswap %%eax" : "=a"(n) : "a"(n)); return n; #elif JUCE_USE_MSVC_INTRINSICS return _byteswap_ulong (n); #elif JUCE_MSVC && ! JUCE_NO_INLINE_ASM __asm { mov eax, n bswap eax mov n, eax } return n; #elif JUCE_ANDROID return bswap_32 (n); #else return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8); #endif } inline uint64 ByteOrder::swap (uint64 value) noexcept { #if JUCE_MAC || JUCE_IOS return OSSwapInt64 (value); #elif JUCE_USE_MSVC_INTRINSICS return _byteswap_uint64 (value); #else return (((uint64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); #endif } #if JUCE_LITTLE_ENDIAN inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return v; } inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return v; } inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return v; } inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); } inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); } inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); } inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast (bytes); } inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast (bytes); } inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast (bytes); } inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } inline bool ByteOrder::isBigEndian() noexcept { return false; } #else inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); } inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return swap (v); } inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return swap (v); } inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; } inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; } inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; } inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast (bytes); } inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast (bytes); } inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast (bytes); } inline bool ByteOrder::isBigEndian() noexcept { return true; } #endif inline int ByteOrder::littleEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast (bytes)[2]) << 16) | (((int) static_cast (bytes)[1]) << 8) | ((int) static_cast (bytes)[0]); } inline int ByteOrder::bigEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast (bytes)[0]) << 16) | (((int) static_cast (bytes)[1]) << 8) | ((int) static_cast (bytes)[2]); } inline void ByteOrder::littleEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast (destBytes)[0] = (uint8) value; static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) (value >> 16); } inline void ByteOrder::bigEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast (destBytes)[0] = (uint8) (value >> 16); static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) value; } #endif // JUCE_BYTEORDER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h000066400000000000000000000054711320201440200324650ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CONTAINERDELETEPOLICY_H_INCLUDED #define JUCE_CONTAINERDELETEPOLICY_H_INCLUDED //============================================================================== /** Used by container classes as an indirect way to delete an object of a particular type. The generic implementation of this class simply calls 'delete', but you can create a specialised version of it for a particular class if you need to delete that type of object in a more appropriate way. @see ScopedPointer, OwnedArray */ template struct ContainerDeletePolicy { static void destroy (ObjectType* object) { // If the line below triggers a compiler error, it means that you are using // an incomplete type for ObjectType (for example, a type that is declared // but not defined). This is a problem because then the following delete is // undefined behaviour. The purpose of the sizeof is to capture this situation. // If this was caused by a ScopedPointer to a forward-declared type, move the // implementation of all methods trying to use the ScopedPointer (e.g. the destructor // of the class owning it) into cpp files where they can see to the definition // of ObjectType. This should fix the error. ignoreUnused (sizeof (ObjectType)); delete object; } }; #endif // JUCE_CONTAINERDELETEPOLICY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h000066400000000000000000000307271320201440200300720ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_HEAPBLOCK_H_INCLUDED #define JUCE_HEAPBLOCK_H_INCLUDED #ifndef DOXYGEN namespace HeapBlockHelper { template struct ThrowOnFail { static void check (void*) {} }; template<> struct ThrowOnFail { static void check (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; } #endif //============================================================================== /** Very simple container class to hold a pointer to some data on the heap. When you need to allocate some heap storage for something, always try to use this class instead of allocating the memory directly using malloc/free. A HeapBlock object can be treated in pretty much exactly the same way as an char*, but as long as you allocate it on the stack or as a class member, it's almost impossible for it to leak memory. It also makes your code much more concise and readable than doing the same thing using direct allocations, E.g. instead of this: @code int* temp = (int*) malloc (1024 * sizeof (int)); memcpy (temp, xyz, 1024 * sizeof (int)); free (temp); temp = (int*) calloc (2048 * sizeof (int)); temp[0] = 1234; memcpy (foobar, temp, 2048 * sizeof (int)); free (temp); @endcode ..you could just write this: @code HeapBlock temp (1024); memcpy (temp, xyz, 1024 * sizeof (int)); temp.calloc (2048); temp[0] = 1234; memcpy (foobar, temp, 2048 * sizeof (int)); @endcode The class is extremely lightweight, containing only a pointer to the data, and exposes malloc/realloc/calloc/free methods that do the same jobs as their less object-oriented counterparts. Despite adding safety, you probably won't sacrifice any performance by using this in place of normal pointers. The throwOnFailure template parameter can be set to true if you'd like the class to throw a std::bad_alloc exception when an allocation fails. If this is false, then a failed allocation will just leave the heapblock with a null pointer (assuming that the system's malloc() function doesn't throw). @see Array, OwnedArray, MemoryBlock */ template class HeapBlock { public: //============================================================================== /** Creates a HeapBlock which is initially just a null pointer. After creation, you can resize the array using the malloc(), calloc(), or realloc() methods. */ HeapBlock() noexcept : data (nullptr) { } /** Creates a HeapBlock containing a number of elements. The contents of the block are undefined, as it will have been created by a malloc call. If you want an array of zero values, you can use the calloc() method or the other constructor that takes an InitialisationState parameter. */ explicit HeapBlock (const size_t numElements) : data (static_cast (std::malloc (numElements * sizeof (ElementType)))) { throwOnAllocationFailure(); } /** Creates a HeapBlock containing a number of elements. The initialiseToZero parameter determines whether the new memory should be cleared, or left uninitialised. */ HeapBlock (const size_t numElements, const bool initialiseToZero) : data (static_cast (initialiseToZero ? std::calloc (numElements, sizeof (ElementType)) : std::malloc (numElements * sizeof (ElementType)))) { throwOnAllocationFailure(); } /** Destructor. This will free the data, if any has been allocated. */ ~HeapBlock() { std::free (data); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS HeapBlock (HeapBlock&& other) noexcept : data (other.data) { other.data = nullptr; } HeapBlock& operator= (HeapBlock&& other) noexcept { std::swap (data, other.data); return *this; } #endif //============================================================================== /** Returns a raw pointer to the allocated data. This may be a null pointer if the data hasn't yet been allocated, or if it has been freed by calling the free() method. */ inline operator ElementType*() const noexcept { return data; } /** Returns a raw pointer to the allocated data. This may be a null pointer if the data hasn't yet been allocated, or if it has been freed by calling the free() method. */ inline ElementType* getData() const noexcept { return data; } /** Returns a void pointer to the allocated data. This may be a null pointer if the data hasn't yet been allocated, or if it has been freed by calling the free() method. */ inline operator void*() const noexcept { return static_cast (data); } /** Returns a void pointer to the allocated data. This may be a null pointer if the data hasn't yet been allocated, or if it has been freed by calling the free() method. */ inline operator const void*() const noexcept { return static_cast (data); } /** Lets you use indirect calls to the first element in the array. Obviously this will cause problems if the array hasn't been initialised, because it'll be referencing a null pointer. */ inline ElementType* operator->() const noexcept { return data; } /** Returns a reference to one of the data elements. Obviously there's no bounds-checking here, as this object is just a dumb pointer and has no idea of the size it currently has allocated. */ template inline ElementType& operator[] (IndexType index) const noexcept { return data [index]; } /** Returns a pointer to a data element at an offset from the start of the array. This is the same as doing pointer arithmetic on the raw pointer itself. */ template inline ElementType* operator+ (IndexType index) const noexcept { return data + index; } //============================================================================== /** Compares the pointer with another pointer. This can be handy for checking whether this is a null pointer. */ inline bool operator== (const ElementType* const otherPointer) const noexcept { return otherPointer == data; } /** Compares the pointer with another pointer. This can be handy for checking whether this is a null pointer. */ inline bool operator!= (const ElementType* const otherPointer) const noexcept { return otherPointer != data; } //============================================================================== /** Allocates a specified amount of memory. This uses the normal malloc to allocate an amount of memory for this object. Any previously allocated memory will be freed by this method. The number of bytes allocated will be (newNumElements * elementSize). Normally you wouldn't need to specify the second parameter, but it can be handy if you need to allocate a size in bytes rather than in terms of the number of elements. The data that is allocated will be freed when this object is deleted, or when you call free() or any of the allocation methods. */ void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { std::free (data); data = static_cast (std::malloc (newNumElements * elementSize)); throwOnAllocationFailure(); } /** Allocates a specified amount of memory and clears it. This does the same job as the malloc() method, but clears the memory that it allocates. */ void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { std::free (data); data = static_cast (std::calloc (newNumElements, elementSize)); throwOnAllocationFailure(); } /** Allocates a specified amount of memory and optionally clears it. This does the same job as either malloc() or calloc(), depending on the initialiseToZero parameter. */ void allocate (const size_t newNumElements, bool initialiseToZero) { std::free (data); data = static_cast (initialiseToZero ? std::calloc (newNumElements, sizeof (ElementType)) : std::malloc (newNumElements * sizeof (ElementType))); throwOnAllocationFailure(); } /** Re-allocates a specified amount of memory. The semantics of this method are the same as malloc() and calloc(), but it uses realloc() to keep as much of the existing data as possible. */ void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { data = static_cast (data == nullptr ? std::malloc (newNumElements * elementSize) : std::realloc (data, newNumElements * elementSize)); throwOnAllocationFailure(); } /** Frees any currently-allocated data. This will free the data and reset this object to be a null pointer. */ void free() noexcept { std::free (data); data = nullptr; } /** Swaps this object's data with the data of another HeapBlock. The two objects simply exchange their data pointers. */ template void swapWith (HeapBlock& other) noexcept { std::swap (data, other.data); } /** This fills the block with zeros, up to the number of elements specified. Since the block has no way of knowing its own size, you must make sure that the number of elements you specify doesn't exceed the allocated size. */ void clear (size_t numElements) noexcept { zeromem (data, sizeof (ElementType) * numElements); } /** This typedef can be used to get the type of the heapblock's elements. */ typedef ElementType Type; private: //============================================================================== ElementType* data; void throwOnAllocationFailure() const { HeapBlockHelper::ThrowOnFail::check (data); } #if ! (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) JUCE_DECLARE_NON_COPYABLE (HeapBlock) JUCE_PREVENT_HEAP_ALLOCATION // Creating a 'new HeapBlock' would be missing the point! #endif }; #endif // JUCE_HEAPBLOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h000066400000000000000000000135061320201440200322440ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED #define JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED //============================================================================== /** Embedding an instance of this class inside another class can be used as a low-overhead way of detecting leaked instances. This class keeps an internal static count of the number of instances that are active, so that when the app is shutdown and the static destructors are called, it can check whether there are any left-over instances that may have been leaked. To use it, use the JUCE_LEAK_DETECTOR macro as a simple way to put one in your class declaration. Have a look through the juce codebase for examples, it's used in most of the classes. */ template class LeakedObjectDetector { public: //============================================================================== LeakedObjectDetector() noexcept { ++(getCounter().numObjects); } LeakedObjectDetector (const LeakedObjectDetector&) noexcept { ++(getCounter().numObjects); } ~LeakedObjectDetector() { if (--(getCounter().numObjects) < 0) { DBG ("*** Dangling pointer deletion! Class: " << getLeakedObjectClassName()); /** If you hit this, then you've managed to delete more instances of this class than you've created.. That indicates that you're deleting some dangling pointers. Note that although this assertion will have been triggered during a destructor, it might not be this particular deletion that's at fault - the incorrect one may have happened at an earlier point in the program, and simply not been detected until now. Most errors like this are caused by using old-fashioned, non-RAII techniques for your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! */ jassertfalse; } } private: //============================================================================== class LeakCounter { public: LeakCounter() noexcept {} ~LeakCounter() { if (numObjects.value > 0) { DBG ("*** Leaked objects detected: " << numObjects.value << " instance(s) of class " << getLeakedObjectClassName()); /** If you hit this, then you've leaked one or more objects of the type specified by the 'OwnerClass' template parameter - the name should have been printed by the line above. If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! */ jassertfalse; } } Atomic numObjects; }; static const char* getLeakedObjectClassName() { return OwnerClass::getLeakedObjectClassName(); } static LeakCounter& getCounter() noexcept { static LeakCounter counter; return counter; } }; //============================================================================== #if DOXYGEN || ! defined (JUCE_LEAK_DETECTOR) #if (DOXYGEN || JUCE_CHECK_MEMORY_LEAKS) /** This macro lets you embed a leak-detecting object inside a class. To use it, simply declare a JUCE_LEAK_DETECTOR(YourClassName) inside a private section of the class declaration. E.g. @code class MyClass { public: MyClass(); void blahBlah(); private: JUCE_LEAK_DETECTOR (MyClass) }; @endcode @see JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR, LeakedObjectDetector */ #define JUCE_LEAK_DETECTOR(OwnerClass) \ friend class juce::LeakedObjectDetector; \ static const char* getLeakedObjectClassName() noexcept { return #OwnerClass; } \ juce::LeakedObjectDetector JUCE_JOIN_MACRO (leakDetector, __LINE__); #else #define JUCE_LEAK_DETECTOR(OwnerClass) #endif #endif #endif // JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h000066400000000000000000000146501320201440200275070ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_MEMORY_H_INCLUDED #define JUCE_MEMORY_H_INCLUDED //============================================================================== /** Fills a block of memory with zeros. */ inline void zeromem (void* memory, size_t numBytes) noexcept { memset (memory, 0, numBytes); } /** Overwrites a structure or object with zeros. */ template inline void zerostruct (Type& structure) noexcept { memset (&structure, 0, sizeof (structure)); } /** Delete an object pointer, and sets the pointer to null. Remember that it's not good c++ practice to use delete directly - always try to use a ScopedPointer or other automatic lifetime-management system rather than resorting to deleting raw pointers! */ template inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; } /** A handy function which adds a number of bytes to any type of pointer and returns the result. This can be useful to avoid casting pointers to a char* and back when you want to move them by a specific number of bytes, */ template inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return (Type*) (((char*) basePointer) + bytes); } /** A handy function which returns the difference between any two pointers, in bytes. The address of the second pointer is subtracted from the first, and the difference in bytes is returned. */ template inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { return (int) (((const char*) pointer1) - (const char*) pointer2); } /** If a pointer is non-null, this returns a new copy of the object that it points to, or safely returns nullptr if the pointer is null. */ template inline Type* createCopyIfNotNull (const Type* objectToCopy) { return objectToCopy != nullptr ? new Type (*objectToCopy) : nullptr; } //============================================================================== /** A handy function to read un-aligned memory without a performance penalty or bus-error. */ template inline Type readUnaligned (const void* srcPtr) noexcept { Type value; memcpy (&value, srcPtr, sizeof (Type)); return value; } /** A handy function to write un-aligned memory without a performance penalty or bus-error. */ template inline void writeUnaligned (void* dstPtr, Type value) noexcept { memcpy (dstPtr, &value, sizeof(Type)); } //============================================================================== #if JUCE_MAC || JUCE_IOS || DOXYGEN /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack. */ class JUCE_API ScopedAutoReleasePool { public: ScopedAutoReleasePool(); ~ScopedAutoReleasePool(); private: void* pool; JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool) }; /** A macro that can be used to easily declare a local ScopedAutoReleasePool object for RAII-based obj-C autoreleasing. Because this may use the \@autoreleasepool syntax, you must follow the macro with a set of braces to mark the scope of the pool. */ #if (JUCE_COMPILER_SUPPORTS_ARC && defined (__OBJC__)) || DOXYGEN #define JUCE_AUTORELEASEPOOL @autoreleasepool #else #define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__); #endif #else #define JUCE_AUTORELEASEPOOL #endif //============================================================================== /* In a Windows DLL build, we'll expose some malloc/free functions that live inside the DLL, and use these for allocating all the objects - that way all juce objects in the DLL and in the host will live in the same heap, avoiding problems when an object is created in one module and passed across to another where it is deleted. By piggy-backing on the JUCE_LEAK_DETECTOR macro, these allocators can be injected into most juce classes. */ #if JUCE_MSVC && (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) && ! (JUCE_DISABLE_DLL_ALLOCATORS || DOXYGEN) extern JUCE_API void* juceDLL_malloc (size_t); extern JUCE_API void juceDLL_free (void*); #define JUCE_LEAK_DETECTOR(OwnerClass) public:\ static void* operator new (size_t sz) { return juce::juceDLL_malloc (sz); } \ static void* operator new (size_t, void* p) { return p; } \ static void operator delete (void* p) { juce::juceDLL_free (p); } \ static void operator delete (void*, void*) {} #endif //============================================================================== /** (Deprecated) This was a Windows-specific way of checking for object leaks - now please use the JUCE_LEAK_DETECTOR instead. */ #ifndef juce_UseDebuggingNewOperator #define juce_UseDebuggingNewOperator #endif #endif // JUCE_MEMORY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp000066400000000000000000000272311320201440200310140ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ MemoryBlock::MemoryBlock() noexcept : size (0) { } MemoryBlock::MemoryBlock (const size_t initialSize, const bool initialiseToZero) { if (initialSize > 0) { size = initialSize; data.allocate (initialSize, initialiseToZero); } else { size = 0; } } MemoryBlock::MemoryBlock (const MemoryBlock& other) : size (other.size) { if (size > 0) { jassert (other.data != nullptr); data.malloc (size); memcpy (data, other.data, size); } } MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom, const size_t sizeInBytes) : size (sizeInBytes) { jassert (((ssize_t) sizeInBytes) >= 0); if (size > 0) { jassert (dataToInitialiseFrom != nullptr); // non-zero size, but a zero pointer passed-in? data.malloc (size); if (dataToInitialiseFrom != nullptr) memcpy (data, dataToInitialiseFrom, size); } } MemoryBlock::~MemoryBlock() noexcept { } MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) { if (this != &other) { setSize (other.size, false); memcpy (data, other.data, size); } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept : data (static_cast&&> (other.data)), size (other.size) { } MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept { data = static_cast&&> (other.data); size = other.size; return *this; } #endif //============================================================================== bool MemoryBlock::operator== (const MemoryBlock& other) const noexcept { return matches (other.data, other.size); } bool MemoryBlock::operator!= (const MemoryBlock& other) const noexcept { return ! operator== (other); } bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const noexcept { return size == dataSize && memcmp (data, dataToCompare, size) == 0; } //============================================================================== // this will resize the block to this size void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero) { if (size != newSize) { if (newSize <= 0) { reset(); } else { if (data != nullptr) { data.realloc (newSize); if (initialiseToZero && (newSize > size)) zeromem (data + size, newSize - size); } else { data.allocate (newSize, initialiseToZero); } size = newSize; } } } void MemoryBlock::reset() { data.free(); size = 0; } void MemoryBlock::ensureSize (const size_t minimumSize, const bool initialiseToZero) { if (size < minimumSize) setSize (minimumSize, initialiseToZero); } void MemoryBlock::swapWith (MemoryBlock& other) noexcept { std::swap (size, other.size); data.swapWith (other.data); } //============================================================================== void MemoryBlock::fillWith (const uint8 value) noexcept { memset (data, (int) value, size); } void MemoryBlock::append (const void* const srcData, const size_t numBytes) { if (numBytes > 0) { jassert (srcData != nullptr); // this must not be null! const size_t oldSize = size; setSize (size + numBytes); memcpy (data + oldSize, srcData, numBytes); } } void MemoryBlock::replaceWith (const void* const srcData, const size_t numBytes) { if (numBytes > 0) { jassert (srcData != nullptr); // this must not be null! setSize (numBytes); memcpy (data, srcData, numBytes); } } void MemoryBlock::insert (const void* const srcData, const size_t numBytes, size_t insertPosition) { if (numBytes > 0) { jassert (srcData != nullptr); // this must not be null! insertPosition = jmin (size, insertPosition); const size_t trailingDataSize = size - insertPosition; setSize (size + numBytes, false); if (trailingDataSize > 0) memmove (data + insertPosition + numBytes, data + insertPosition, trailingDataSize); memcpy (data + insertPosition, srcData, numBytes); } } void MemoryBlock::removeSection (const size_t startByte, const size_t numBytesToRemove) { if (startByte + numBytesToRemove >= size) { setSize (startByte); } else if (numBytesToRemove > 0) { memmove (data + startByte, data + startByte + numBytesToRemove, size - (startByte + numBytesToRemove)); setSize (size - numBytesToRemove); } } void MemoryBlock::copyFrom (const void* const src, int offset, size_t num) noexcept { const char* d = static_cast (src); if (offset < 0) { d -= offset; num += (size_t) -offset; offset = 0; } if ((size_t) offset + num > size) num = size - (size_t) offset; if (num > 0) memcpy (data + offset, d, num); } void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcept { char* d = static_cast (dst); if (offset < 0) { zeromem (d, (size_t) -offset); d -= offset; num -= (size_t) -offset; offset = 0; } if ((size_t) offset + num > size) { const size_t newNum = (size_t) size - (size_t) offset; zeromem (d + newNum, num - newNum); num = newNum; } if (num > 0) memcpy (d, data + offset, num); } String MemoryBlock::toString() const { return String::fromUTF8 (data, (int) size); } //============================================================================== int MemoryBlock::getBitRange (const size_t bitRangeStart, size_t numBits) const noexcept { int res = 0; size_t byte = bitRangeStart >> 3; size_t offsetInByte = bitRangeStart & 7; size_t bitsSoFar = 0; while (numBits > 0 && (size_t) byte < size) { const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte); const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte; res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar); bitsSoFar += bitsThisTime; numBits -= bitsThisTime; ++byte; offsetInByte = 0; } return res; } void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int bitsToSet) noexcept { size_t byte = bitRangeStart >> 3; size_t offsetInByte = bitRangeStart & 7; uint32 mask = ~((((uint32) 0xffffffff) << (32 - numBits)) >> (32 - numBits)); while (numBits > 0 && (size_t) byte < size) { const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte); const uint32 tempMask = (mask << offsetInByte) | ~((((uint32) 0xffffffff) >> offsetInByte) << offsetInByte); const uint32 tempBits = (uint32) bitsToSet << offsetInByte; data[byte] = (char) (((uint32) data[byte] & tempMask) | tempBits); ++byte; numBits -= bitsThisTime; bitsToSet >>= bitsThisTime; mask >>= bitsThisTime; offsetInByte = 0; } } //============================================================================== void MemoryBlock::loadFromHexString (StringRef hex) { ensureSize ((size_t) hex.length() >> 1); char* dest = data; String::CharPointerType t (hex.text); for (;;) { int byte = 0; for (int loop = 2; --loop >= 0;) { byte <<= 4; for (;;) { const juce_wchar c = t.getAndAdvance(); if (c >= '0' && c <= '9') { byte |= c - '0'; break; } if (c >= 'a' && c <= 'z') { byte |= c - ('a' - 10); break; } if (c >= 'A' && c <= 'Z') { byte |= c - ('A' - 10); break; } if (c == 0) { setSize (static_cast (dest - data)); return; } } } *dest++ = (char) byte; } } //============================================================================== static const char base64EncodingTable[] = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; String MemoryBlock::toBase64Encoding() const { const size_t numChars = ((size << 3) + 5) / 6; String destString ((unsigned int) size); // store the length, followed by a '.', and then the data. const int initialLen = destString.length(); destString.preallocateBytes (sizeof (String::CharPointerType::CharType) * (size_t) initialLen + 2 + numChars); String::CharPointerType d (destString.getCharPointer()); d += initialLen; d.write ('.'); for (size_t i = 0; i < numChars; ++i) d.write ((juce_wchar) (uint8) base64EncodingTable [getBitRange (i * 6, 6)]); d.writeNull(); return destString; } static const char base64DecodingTable[] = { 63, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 }; bool MemoryBlock::fromBase64Encoding (StringRef s) { String::CharPointerType dot (CharacterFunctions::find (s.text, (juce_wchar) '.')); if (dot.isEmpty()) return false; const int numBytesNeeded = String (s.text, dot).getIntValue(); setSize ((size_t) numBytesNeeded, true); String::CharPointerType srcChars (dot + 1); int pos = 0; for (;;) { int c = (int) srcChars.getAndAdvance(); if (c == 0) return true; c -= 43; if (isPositiveAndBelow (c, numElementsInArray (base64DecodingTable))) { setBitRange ((size_t) pos, 6, base64DecodingTable [c]); pos += 6; } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h000066400000000000000000000254361320201440200304660ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_MEMORYBLOCK_H_INCLUDED #define JUCE_MEMORYBLOCK_H_INCLUDED //============================================================================== /** A class to hold a resizable block of raw data. */ class JUCE_API MemoryBlock { public: //============================================================================== /** Create an uninitialised block with 0 size. */ MemoryBlock() noexcept; /** Creates a memory block with a given initial size. @param initialSize the size of block to create @param initialiseToZero whether to clear the memory or just leave it uninitialised */ MemoryBlock (const size_t initialSize, bool initialiseToZero = false); /** Creates a copy of another memory block. */ MemoryBlock (const MemoryBlock&); /** Creates a memory block using a copy of a block of data. @param dataToInitialiseFrom some data to copy into this block @param sizeInBytes how much space to use */ MemoryBlock (const void* dataToInitialiseFrom, size_t sizeInBytes); /** Destructor. */ ~MemoryBlock() noexcept; /** Copies another memory block onto this one. This block will be resized and copied to exactly match the other one. */ MemoryBlock& operator= (const MemoryBlock&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MemoryBlock (MemoryBlock&&) noexcept; MemoryBlock& operator= (MemoryBlock&&) noexcept; #endif //============================================================================== /** Compares two memory blocks. @returns true only if the two blocks are the same size and have identical contents. */ bool operator== (const MemoryBlock& other) const noexcept; /** Compares two memory blocks. @returns true if the two blocks are different sizes or have different contents. */ bool operator!= (const MemoryBlock& other) const noexcept; /** Returns true if the data in this MemoryBlock matches the raw bytes passed-in. */ bool matches (const void* data, size_t dataSize) const noexcept; //============================================================================== /** Returns a void pointer to the data. Note that the pointer returned will probably become invalid when the block is resized. */ void* getData() const noexcept { return data; } /** Returns a byte from the memory block. This returns a reference, so you can also use it to set a byte. */ template char& operator[] (const Type offset) const noexcept { return data [offset]; } //============================================================================== /** Returns the block's current allocated size, in bytes. */ size_t getSize() const noexcept { return size; } /** Resizes the memory block. Any data that is present in both the old and new sizes will be retained. When enlarging the block, the new space that is allocated at the end can either be cleared, or left uninitialised. @param newSize the new desired size for the block @param initialiseNewSpaceToZero if the block gets enlarged, this determines whether to clear the new section or just leave it uninitialised @see ensureSize */ void setSize (const size_t newSize, bool initialiseNewSpaceToZero = false); /** Increases the block's size only if it's smaller than a given size. @param minimumSize if the block is already bigger than this size, no action will be taken; otherwise it will be increased to this size @param initialiseNewSpaceToZero if the block gets enlarged, this determines whether to clear the new section or just leave it uninitialised @see setSize */ void ensureSize (const size_t minimumSize, bool initialiseNewSpaceToZero = false); /** Frees all the blocks data, setting its size to 0. */ void reset(); //============================================================================== /** Fills the entire memory block with a repeated byte value. This is handy for clearing a block of memory to zero. */ void fillWith (uint8 valueToUse) noexcept; /** Adds another block of data to the end of this one. The data pointer must not be null. This block's size will be increased accordingly. */ void append (const void* data, size_t numBytes); /** Resizes this block to the given size and fills its contents from the supplied buffer. The data pointer must not be null. */ void replaceWith (const void* data, size_t numBytes); /** Inserts some data into the block. The dataToInsert pointer must not be null. This block's size will be increased accordingly. If the insert position lies outside the valid range of the block, it will be clipped to within the range before being used. */ void insert (const void* dataToInsert, size_t numBytesToInsert, size_t insertPosition); /** Chops out a section of the block. This will remove a section of the memory block and close the gap around it, shifting any subsequent data downwards and reducing the size of the block. If the range specified goes beyond the size of the block, it will be clipped. */ void removeSection (size_t startByte, size_t numBytesToRemove); //============================================================================== /** Copies data into this MemoryBlock from a memory address. @param srcData the memory location of the data to copy into this block @param destinationOffset the offset in this block at which the data being copied should begin @param numBytes how much to copy in (if this goes beyond the size of the memory block, it will be clipped so not to do anything nasty) */ void copyFrom (const void* srcData, int destinationOffset, size_t numBytes) noexcept; /** Copies data from this MemoryBlock to a memory address. @param destData the memory location to write to @param sourceOffset the offset within this block from which the copied data will be read @param numBytes how much to copy (if this extends beyond the limits of the memory block, zeros will be used for that portion of the data) */ void copyTo (void* destData, int sourceOffset, size_t numBytes) const noexcept; //============================================================================== /** Exchanges the contents of this and another memory block. No actual copying is required for this, so it's very fast. */ void swapWith (MemoryBlock& other) noexcept; //============================================================================== /** Attempts to parse the contents of the block as a zero-terminated UTF8 string. */ String toString() const; //============================================================================== /** Parses a string of hexadecimal numbers and writes this data into the memory block. The block will be resized to the number of valid bytes read from the string. Non-hex characters in the string will be ignored. @see String::toHexString() */ void loadFromHexString (StringRef sourceHexString); //============================================================================== /** Sets a number of bits in the memory block, treating it as a long binary sequence. */ void setBitRange (size_t bitRangeStart, size_t numBits, int binaryNumberToApply) noexcept; /** Reads a number of bits from the memory block, treating it as one long binary sequence */ int getBitRange (size_t bitRangeStart, size_t numBitsToRead) const noexcept; //============================================================================== /** Returns a string of characters that represent the binary contents of this block. Uses a 64-bit encoding system to allow binary data to be turned into a string of simple non-extended characters, e.g. for storage in XML. @see fromBase64Encoding */ String toBase64Encoding() const; /** Takes a string of encoded characters and turns it into binary data. The string passed in must have been created by to64BitEncoding(), and this block will be resized to recreate the original data block. @see toBase64Encoding */ bool fromBase64Encoding (StringRef encodedString); private: //============================================================================== HeapBlock data; size_t size; JUCE_LEAK_DETECTOR (MemoryBlock) }; #endif // JUCE_MEMORYBLOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h000066400000000000000000000165551320201440200325310ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED #define JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED //============================================================================== /** Holds a pointer to an object which can optionally be deleted when this pointer goes out of scope. This acts in many ways like a ScopedPointer, but allows you to specify whether or not the object is deleted. @see ScopedPointer */ template class OptionalScopedPointer { public: //============================================================================== /** Creates an empty OptionalScopedPointer. */ OptionalScopedPointer() : shouldDelete (false) {} /** Creates an OptionalScopedPointer to point to a given object, and specifying whether the OptionalScopedPointer will delete it. If takeOwnership is true, then the OptionalScopedPointer will act like a ScopedPointer, deleting the object when it is itself deleted. If this parameter is false, then the OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. */ OptionalScopedPointer (ObjectType* objectToHold, bool takeOwnership) : object (objectToHold), shouldDelete (takeOwnership) { } /** Takes ownership of the object that another OptionalScopedPointer holds. Like a normal ScopedPointer, the objectToTransferFrom object will become null, as ownership of the managed object is transferred to this object. The flag to indicate whether or not to delete the managed object is also copied from the source object. */ OptionalScopedPointer (OptionalScopedPointer& objectToTransferFrom) : object (objectToTransferFrom.release()), shouldDelete (objectToTransferFrom.shouldDelete) { } /** Takes ownership of the object that another OptionalScopedPointer holds. Like a normal ScopedPointer, the objectToTransferFrom object will become null, as ownership of the managed object is transferred to this object. The ownership flag that says whether or not to delete the managed object is also copied from the source object. */ OptionalScopedPointer& operator= (OptionalScopedPointer& objectToTransferFrom) { if (object != objectToTransferFrom.object) { clear(); object = objectToTransferFrom.object; } shouldDelete = objectToTransferFrom.shouldDelete; return *this; } /** The destructor may or may not delete the object that is being held, depending on the takeOwnership flag that was specified when the object was first passed into an OptionalScopedPointer constructor. */ ~OptionalScopedPointer() { clear(); } //============================================================================== /** Returns the object that this pointer is managing. */ inline operator ObjectType*() const noexcept { return object; } /** Returns the object that this pointer is managing. */ inline ObjectType* get() const noexcept { return object; } /** Returns the object that this pointer is managing. */ inline ObjectType& operator*() const noexcept { return *object; } /** Lets you access methods and properties of the object that this pointer is holding. */ inline ObjectType* operator->() const noexcept { return object; } //============================================================================== /** Removes the current object from this OptionalScopedPointer without deleting it. This will return the current object, and set this OptionalScopedPointer to a null pointer. */ ObjectType* release() noexcept { return object.release(); } /** Resets this pointer to null, possibly deleting the object that it holds, if it has ownership of it. */ void clear() { if (! shouldDelete) object.release(); } /** Makes this OptionalScopedPointer point at a new object, specifying whether the OptionalScopedPointer will take ownership of the object. If takeOwnership is true, then the OptionalScopedPointer will act like a ScopedPointer, deleting the object when it is itself deleted. If this parameter is false, then the OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. */ void set (ObjectType* newObject, bool takeOwnership) { if (object != newObject) { clear(); object = newObject; } shouldDelete = takeOwnership; } /** Makes this OptionalScopedPointer point at a new object, and take ownership of that object. */ void setOwned (ObjectType* newObject) { set (newObject, true); } /** Makes this OptionalScopedPointer point at a new object, but will not take ownership of that object. */ void setNonOwned (ObjectType* newObject) { set (newObject, false); } /** Returns true if the target object will be deleted when this pointer object is deleted. */ bool willDeleteObject() const noexcept { return shouldDelete; } //============================================================================== /** Swaps this object with another OptionalScopedPointer. The two objects simply exchange their states. */ void swapWith (OptionalScopedPointer& other) noexcept { object.swapWith (other.object); std::swap (shouldDelete, other.shouldDelete); } private: //============================================================================== ScopedPointer object; bool shouldDelete; }; #endif // JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h000066400000000000000000000354551320201440200326140ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED #define JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED //============================================================================== /** A base class which provides methods for reference-counting. To add reference-counting to a class, derive it from this class, and use the ReferenceCountedObjectPtr class to point to it. e.g. @code class MyClass : public ReferenceCountedObject { void foo(); // This is a neat way of declaring a typedef for a pointer class, // rather than typing out the full templated name each time.. typedef ReferenceCountedObjectPtr Ptr; }; MyClass::Ptr p = new MyClass(); MyClass::Ptr p2 = p; p = nullptr; p2->foo(); @endcode Once a new ReferenceCountedObject has been assigned to a pointer, be careful not to delete the object manually. This class uses an Atomic value to hold the reference count, so that it the pointers can be passed between threads safely. For a faster but non-thread-safe version, use SingleThreadedReferenceCountedObject instead. @see ReferenceCountedObjectPtr, ReferenceCountedArray, SingleThreadedReferenceCountedObject */ class JUCE_API ReferenceCountedObject { public: //============================================================================== /** Increments the object's reference count. This is done automatically by the smart pointer, but is public just in case it's needed for nefarious purposes. */ void incReferenceCount() noexcept { ++refCount; } /** Decreases the object's reference count. If the count gets to zero, the object will be deleted. */ void decReferenceCount() noexcept { jassert (getReferenceCount() > 0); if (--refCount == 0) delete this; } /** Decreases the object's reference count. If the count gets to zero, the object will not be deleted, but this method will return true, allowing the caller to take care of deletion. */ bool decReferenceCountWithoutDeleting() noexcept { jassert (getReferenceCount() > 0); return --refCount == 0; } /** Returns the object's current reference count. */ int getReferenceCount() const noexcept { return refCount.get(); } protected: //============================================================================== /** Creates the reference-counted object (with an initial ref count of zero). */ ReferenceCountedObject() {} /** Destructor. */ virtual ~ReferenceCountedObject() { // it's dangerous to delete an object that's still referenced by something else! jassert (getReferenceCount() == 0); } /** Resets the reference count to zero without deleting the object. You should probably never need to use this! */ void resetReferenceCount() noexcept { refCount = 0; } private: //============================================================================== Atomic refCount; friend struct ContainerDeletePolicy; JUCE_DECLARE_NON_COPYABLE (ReferenceCountedObject) }; //============================================================================== /** Adds reference-counting to an object. This is effectively a version of the ReferenceCountedObject class, but which uses a non-atomic counter, and so is not thread-safe (but which will be more efficient). For more details on how to use it, see the ReferenceCountedObject class notes. @see ReferenceCountedObject, ReferenceCountedObjectPtr, ReferenceCountedArray */ class JUCE_API SingleThreadedReferenceCountedObject { public: //============================================================================== /** Increments the object's reference count. This is done automatically by the smart pointer, but is public just in case it's needed for nefarious purposes. */ void incReferenceCount() noexcept { ++refCount; } /** Decreases the object's reference count. If the count gets to zero, the object will be deleted. */ void decReferenceCount() noexcept { jassert (getReferenceCount() > 0); if (--refCount == 0) delete this; } /** Decreases the object's reference count. If the count gets to zero, the object will not be deleted, but this method will return true, allowing the caller to take care of deletion. */ bool decReferenceCountWithoutDeleting() noexcept { jassert (getReferenceCount() > 0); return --refCount == 0; } /** Returns the object's current reference count. */ int getReferenceCount() const noexcept { return refCount; } protected: //============================================================================== /** Creates the reference-counted object (with an initial ref count of zero). */ SingleThreadedReferenceCountedObject() : refCount (0) {} /** Destructor. */ virtual ~SingleThreadedReferenceCountedObject() { // it's dangerous to delete an object that's still referenced by something else! jassert (getReferenceCount() == 0); } private: //============================================================================== int refCount; friend struct ContainerDeletePolicy; JUCE_DECLARE_NON_COPYABLE (SingleThreadedReferenceCountedObject) }; //============================================================================== /** A smart-pointer class which points to a reference-counted object. The template parameter specifies the class of the object you want to point to - the easiest way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods should behave. When using this class, you'll probably want to create a typedef to abbreviate the full templated name - e.g. @code struct MyClass : public ReferenceCountedObject { typedef ReferenceCountedObjectPtr Ptr; ... @endcode @see ReferenceCountedObject, ReferenceCountedObjectArray */ template class ReferenceCountedObjectPtr { public: /** The class being referenced by this pointer. */ typedef ReferenceCountedObjectClass ReferencedType; //============================================================================== /** Creates a pointer to a null object. */ ReferenceCountedObjectPtr() noexcept : referencedObject (nullptr) { } /** Creates a pointer to an object. This will increment the object's reference-count. */ ReferenceCountedObjectPtr (ReferencedType* refCountedObject) noexcept : referencedObject (refCountedObject) { incIfNotNull (refCountedObject); } /** Copies another pointer. This will increment the object's reference-count. */ ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept : referencedObject (other.referencedObject) { incIfNotNull (referencedObject); } /** Copies another pointer. This will increment the object's reference-count (if it is non-null). */ template ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept : referencedObject (static_cast (other.get())) { incIfNotNull (referencedObject); } /** Changes this pointer to point at a different object. The reference count of the old object is decremented, and it might be deleted if it hits zero. The new object's count is incremented. */ ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other) { return operator= (other.referencedObject); } /** Changes this pointer to point at a different object. The reference count of the old object is decremented, and it might be deleted if it hits zero. The new object's count is incremented. */ template ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other) { return operator= (static_cast (other.get())); } /** Changes this pointer to point at a different object. The reference count of the old object is decremented, and it might be deleted if it hits zero. The new object's count is incremented. */ ReferenceCountedObjectPtr& operator= (ReferencedType* const newObject) { if (referencedObject != newObject) { incIfNotNull (newObject); ReferencedType* const oldObject = referencedObject; referencedObject = newObject; decIfNotNull (oldObject); } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS /** Takes-over the object from another pointer. */ ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept : referencedObject (other.referencedObject) { other.referencedObject = nullptr; } /** Takes-over the object from another pointer. */ ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other) { std::swap (referencedObject, other.referencedObject); return *this; } #endif /** Destructor. This will decrement the object's reference-count, which will cause the object to be deleted when the ref-count hits zero. */ ~ReferenceCountedObjectPtr() { decIfNotNull (referencedObject); } //============================================================================== /** Returns the object that this pointer references. The pointer returned may be null, of course. */ operator ReferencedType*() const noexcept { return referencedObject; } /** Returns the object that this pointer references. The pointer returned may be null, of course. */ ReferencedType* get() const noexcept { return referencedObject; } /** Returns the object that this pointer references. The pointer returned may be null, of course. */ ReferencedType* getObject() const noexcept { return referencedObject; } // the -> operator is called on the referenced object ReferencedType* operator->() const noexcept { jassert (referencedObject != nullptr); // null pointer method call! return referencedObject; } private: //============================================================================== ReferencedType* referencedObject; static void incIfNotNull (ReferencedType* o) noexcept { if (o != nullptr) o->incReferenceCount(); } static void decIfNotNull (ReferencedType* o) noexcept { if (o != nullptr && o->decReferenceCountWithoutDeleting()) ContainerDeletePolicy::destroy (o); } }; //============================================================================== /** Compares two ReferenceCountedObjectPointers. */ template bool operator== (const ReferenceCountedObjectPtr& object1, ReferenceCountedObjectClass* const object2) noexcept { return object1.get() == object2; } /** Compares two ReferenceCountedObjectPointers. */ template bool operator== (const ReferenceCountedObjectPtr& object1, const ReferenceCountedObjectPtr& object2) noexcept { return object1.get() == object2.get(); } /** Compares two ReferenceCountedObjectPointers. */ template bool operator== (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr& object2) noexcept { return object1 == object2.get(); } /** Compares two ReferenceCountedObjectPointers. */ template bool operator!= (const ReferenceCountedObjectPtr& object1, const ReferenceCountedObjectClass* object2) noexcept { return object1.get() != object2; } /** Compares two ReferenceCountedObjectPointers. */ template bool operator!= (const ReferenceCountedObjectPtr& object1, const ReferenceCountedObjectPtr& object2) noexcept { return object1.get() != object2.get(); } /** Compares two ReferenceCountedObjectPointers. */ template bool operator!= (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr& object2) noexcept { return object1 != object2.get(); } #endif // JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h000066400000000000000000000264451320201440200310220ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SCOPEDPOINTER_H_INCLUDED #define JUCE_SCOPEDPOINTER_H_INCLUDED //============================================================================== /** This class holds a pointer which is automatically deleted when this object goes out of scope. Once a pointer has been passed to a ScopedPointer, it will make sure that the pointer gets deleted when the ScopedPointer is deleted. Using the ScopedPointer on the stack or as member variables is a good way to use RAII to avoid accidentally leaking dynamically created objects. A ScopedPointer can be used in pretty much the same way that you'd use a normal pointer to an object. If you use the assignment operator to assign a different object to a ScopedPointer, the old one will be automatically deleted. Important note: The class is designed to hold a pointer to an object, NOT to an array! It calls delete on its payload, not delete[], so do not give it an array to hold! For that kind of purpose, you should be using HeapBlock or Array instead. A const ScopedPointer is guaranteed not to lose ownership of its object or change the object to which it points during its lifetime. This means that making a copy of a const ScopedPointer is impossible, as that would involve the new copy taking ownership from the old one. If you need to get a pointer out of a ScopedPointer without it being deleted, you can use the release() method. Something to note is the main difference between this class and the std::auto_ptr class, which is that ScopedPointer provides a cast-to-object operator, wheras std::auto_ptr requires that you always call get() to retrieve the pointer. The advantages of providing the cast is that you don't need to call get(), so can use the ScopedPointer in pretty much exactly the same way as a raw pointer. The disadvantage is that the compiler is free to use the cast in unexpected and sometimes dangerous ways - in particular, it becomes difficult to return a ScopedPointer as the result of a function. To avoid this causing errors, ScopedPointer contains an overloaded constructor that should cause a syntax error in these circumstances, but it does mean that instead of returning a ScopedPointer from a function, you'd need to return a raw pointer (or use a std::auto_ptr instead). */ template class ScopedPointer { public: //============================================================================== /** Creates a ScopedPointer containing a null pointer. */ inline ScopedPointer() noexcept : object (nullptr) { } /** Creates a ScopedPointer that owns the specified object. */ inline ScopedPointer (ObjectType* const objectToTakePossessionOf) noexcept : object (objectToTakePossessionOf) { } /** Creates a ScopedPointer that takes its pointer from another ScopedPointer. Because a pointer can only belong to one ScopedPointer, this transfers the pointer from the other object to this one, and the other object is reset to be a null pointer. */ ScopedPointer (ScopedPointer& objectToTransferFrom) noexcept : object (objectToTransferFrom.object) { objectToTransferFrom.object = nullptr; } /** Destructor. This will delete the object that this ScopedPointer currently refers to. */ inline ~ScopedPointer() { ContainerDeletePolicy::destroy (object); } /** Changes this ScopedPointer to point to a new object. Because a pointer can only belong to one ScopedPointer, this transfers the pointer from the other object to this one, and the other object is reset to be a null pointer. If this ScopedPointer already points to an object, that object will first be deleted. */ ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) { if (this != objectToTransferFrom.getAddress()) { // Two ScopedPointers should never be able to refer to the same object - if // this happens, you must have done something dodgy! jassert (object == nullptr || object != objectToTransferFrom.object); ObjectType* const oldObject = object; object = objectToTransferFrom.object; objectToTransferFrom.object = nullptr; ContainerDeletePolicy::destroy (oldObject); } return *this; } /** Changes this ScopedPointer to point to a new object. If this ScopedPointer already points to an object, that object will first be deleted. The pointer that you pass in may be a nullptr. */ ScopedPointer& operator= (ObjectType* const newObjectToTakePossessionOf) { if (object != newObjectToTakePossessionOf) { ObjectType* const oldObject = object; object = newObjectToTakePossessionOf; ContainerDeletePolicy::destroy (oldObject); } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS ScopedPointer (ScopedPointer&& other) noexcept : object (other.object) { other.object = nullptr; } ScopedPointer& operator= (ScopedPointer&& other) noexcept { object = other.object; other.object = nullptr; return *this; } #endif //============================================================================== /** Returns the object that this ScopedPointer refers to. */ inline operator ObjectType*() const noexcept { return object; } /** Returns the object that this ScopedPointer refers to. */ inline ObjectType* get() const noexcept { return object; } /** Returns the object that this ScopedPointer refers to. */ inline ObjectType& operator*() const noexcept { return *object; } /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ inline ObjectType* operator->() const noexcept { return object; } //============================================================================== /** Removes the current object from this ScopedPointer without deleting it. This will return the current object, and set the ScopedPointer to a null pointer. */ ObjectType* release() noexcept { ObjectType* const o = object; object = nullptr; return o; } //============================================================================== /** Swaps this object with that of another ScopedPointer. The two objects simply exchange their pointers. */ void swapWith (ScopedPointer& other) noexcept { // Two ScopedPointers should never be able to refer to the same object - if // this happens, you must have done something dodgy! jassert (object != other.object || this == other.getAddress() || object == nullptr); std::swap (object, other.object); } /** If the pointer is non-null, this will attempt to return a new copy of the object that is pointed to. If the pointer is null, this will safely return a nullptr. */ inline ObjectType* createCopy() const { return createCopyIfNotNull (object); } private: //============================================================================== ObjectType* object; // (Required as an alternative to the overloaded & operator). const ScopedPointer* getAddress() const noexcept { return this; } #if ! JUCE_MSVC // (MSVC can't deal with multiple copy constructors) /* The copy constructors are private to stop people accidentally copying a const ScopedPointer (the compiler would let you do so by implicitly casting the source to its raw object pointer). A side effect of this is that in a compiler that doesn't support C++11, you may hit an error when you write something like this: ScopedPointer m = new MyClass(); // Compile error: copy constructor is private. Even though the compiler would normally ignore the assignment here, it can't do so when the copy constructor is private. It's very easy to fix though - just write it like this: ScopedPointer m (new MyClass()); // Compiles OK It's probably best to use the latter form when writing your object declarations anyway, as this is a better representation of the code that you actually want the compiler to produce. */ JUCE_DECLARE_NON_COPYABLE (ScopedPointer) #endif }; //============================================================================== /** Compares a ScopedPointer with another pointer. This can be handy for checking whether this is a null pointer. */ template bool operator== (const ScopedPointer& pointer1, ObjectType* const pointer2) noexcept { return static_cast (pointer1) == pointer2; } /** Compares a ScopedPointer with another pointer. This can be handy for checking whether this is a null pointer. */ template bool operator!= (const ScopedPointer& pointer1, ObjectType* const pointer2) noexcept { return static_cast (pointer1) != pointer2; } //============================================================================== #ifndef DOXYGEN // NB: This is just here to prevent any silly attempts to call deleteAndZero() on a ScopedPointer. template void deleteAndZero (ScopedPointer&) { static_jassert (sizeof (Type) == 12345); } #endif #endif // JUCE_SCOPEDPOINTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h000066400000000000000000000142531320201440200325150ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SHAREDRESOURCEPOINTER_H_INCLUDED #define JUCE_SHAREDRESOURCEPOINTER_H_INCLUDED //============================================================================== /** A smart-pointer that automatically creates and manages the lifetime of a shared static instance of a class. The SharedObjectType template type indicates the class to use for the shared object - the only requirements on this class are that it must have a public default constructor and destructor. The SharedResourcePointer offers a pattern that differs from using a singleton or static instance of an object, because it uses reference-counting to make sure that the underlying shared object is automatically created/destroyed according to the number of SharedResourcePointer objects that exist. When the last one is deleted, the underlying object is also immediately destroyed. This allows you to use scoping to manage the lifetime of a shared resource. Note: the construction/deletion of the shared object must not involve any code that makes recursive calls to a SharedResourcePointer, or you'll cause a deadlock. Example: @code // An example of a class that contains the shared data you want to use. struct MySharedData { // There's no need to ever create an instance of this class directly yourself, // but it does need a public constructor that does the initialisation. MySharedData() { sharedStuff = generateHeavyweightStuff(); } Array sharedStuff; }; struct DataUserClass { DataUserClass() { // Multiple instances of the DataUserClass will all have the same // shared common instance of MySharedData referenced by their sharedData // member variables. useSharedStuff (sharedData->sharedStuff); } // By keeping this pointer as a member variable, the shared resource // is guaranteed to be available for as long as the DataUserClass object. SharedResourcePointer sharedData; }; @endcode */ template class SharedResourcePointer { public: /** Creates an instance of the shared object. If other SharedResourcePointer objects for this type already exist, then this one will simply point to the same shared object that they are already using. Otherwise, if this is the first SharedResourcePointer to be created, then a shared object will be created automatically. */ SharedResourcePointer() { initialise(); } SharedResourcePointer (const SharedResourcePointer&) { initialise(); } /** Destructor. If no other SharedResourcePointer objects exist, this will also delete the shared object to which it refers. */ ~SharedResourcePointer() { SharedObjectHolder& holder = getSharedObjectHolder(); const SpinLock::ScopedLockType sl (holder.lock); if (--(holder.refCount) == 0) holder.sharedInstance = nullptr; } /** Returns the shared object. */ operator SharedObjectType*() const noexcept { return sharedObject; } /** Returns the shared object. */ SharedObjectType& get() const noexcept { return *sharedObject; } /** Returns the object that this pointer references. The pointer returned may be zero, of course. */ SharedObjectType& getObject() const noexcept { return *sharedObject; } SharedObjectType* operator->() const noexcept { return sharedObject; } private: struct SharedObjectHolder : public ReferenceCountedObject { SpinLock lock; ScopedPointer sharedInstance; int refCount; }; static SharedObjectHolder& getSharedObjectHolder() noexcept { static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { 0 }; return *reinterpret_cast (holder); } SharedObjectType* sharedObject; void initialise() { SharedObjectHolder& holder = getSharedObjectHolder(); const SpinLock::ScopedLockType sl (holder.lock); if (++(holder.refCount) == 1) holder.sharedInstance = new SharedObjectType(); sharedObject = holder.sharedInstance; } // There's no need to assign to a SharedResourcePointer because every // instance of the class is exactly the same! SharedResourcePointer& operator= (const SharedResourcePointer&) JUCE_DELETED_FUNCTION; JUCE_LEAK_DETECTOR (SharedResourcePointer) }; #endif // JUCE_SHAREDRESOURCEPOINTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h000066400000000000000000000252001320201440200301720ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SINGLETON_H_INCLUDED #define JUCE_SINGLETON_H_INCLUDED //============================================================================== /** Macro to declare member variables and methods for a singleton class. To use this, add the line juce_DeclareSingleton (MyClass, doNotRecreateAfterDeletion) to the class's definition. Then put a macro juce_ImplementSingleton (MyClass) along with the class's implementation code. It's also a very good idea to also add the call clearSingletonInstance() in your class's destructor, in case it is deleted by other means than deleteInstance() Clients can then call the static method MyClass::getInstance() to get a pointer to the singleton, or MyClass::getInstanceWithoutCreating() which will return nullptr if no instance currently exists. e.g. @code class MySingleton { public: MySingleton() { } ~MySingleton() { // this ensures that no dangling pointers are left when the // singleton is deleted. clearSingletonInstance(); } juce_DeclareSingleton (MySingleton, false) }; juce_ImplementSingleton (MySingleton) // example of usage: MySingleton* m = MySingleton::getInstance(); // creates the singleton if there isn't already one. ... MySingleton::deleteInstance(); // safely deletes the singleton (if it's been created). @endcode If doNotRecreateAfterDeletion = true, it won't allow the object to be created more than once during the process's lifetime - i.e. after you've created and deleted the object, getInstance() will refuse to create another one. This can be useful to stop objects being accidentally re-created during your app's shutdown code. If you know that your object will only be created and deleted by a single thread, you can use the slightly more efficient juce_DeclareSingleton_SingleThreaded() macro instead of this one. @see juce_ImplementSingleton, juce_DeclareSingleton_SingleThreaded */ #define juce_DeclareSingleton(classname, doNotRecreateAfterDeletion) \ \ static classname* _singletonInstance; \ static juce::CriticalSection _singletonLock; \ \ static classname* JUCE_CALLTYPE getInstance() \ { \ if (_singletonInstance == nullptr) \ {\ const juce::ScopedLock sl (_singletonLock); \ \ if (_singletonInstance == nullptr) \ { \ static bool alreadyInside = false; \ static bool createdOnceAlready = false; \ \ const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ jassert (! problem); \ if (! problem) \ { \ createdOnceAlready = true; \ alreadyInside = true; \ classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ alreadyInside = false; \ \ _singletonInstance = newObject; \ } \ } \ } \ \ return _singletonInstance; \ } \ \ static inline classname* JUCE_CALLTYPE getInstanceWithoutCreating() noexcept\ { \ return _singletonInstance; \ } \ \ static void JUCE_CALLTYPE deleteInstance() \ { \ const juce::ScopedLock sl (_singletonLock); \ if (_singletonInstance != nullptr) \ { \ classname* const old = _singletonInstance; \ _singletonInstance = nullptr; \ delete old; \ } \ } \ \ void clearSingletonInstance() noexcept\ { \ if (_singletonInstance == this) \ _singletonInstance = nullptr; \ } //============================================================================== /** This is a counterpart to the juce_DeclareSingleton macro. After adding the juce_DeclareSingleton to the class definition, this macro has to be used in the cpp file. */ #define juce_ImplementSingleton(classname) \ \ classname* classname::_singletonInstance = nullptr; \ juce::CriticalSection classname::_singletonLock; //============================================================================== /** Macro to declare member variables and methods for a singleton class. This is exactly the same as juce_DeclareSingleton, but doesn't use a critical section to make access to it thread-safe. If you know that your object will only ever be created or deleted by a single thread, then this is a more efficient version to use. If doNotRecreateAfterDeletion = true, it won't allow the object to be created more than once during the process's lifetime - i.e. after you've created and deleted the object, getInstance() will refuse to create another one. This can be useful to stop objects being accidentally re-created during your app's shutdown code. See the documentation for juce_DeclareSingleton for more information about how to use it, the only difference being that you have to use juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton, juce_DeclareSingleton_SingleThreaded_Minimal */ #define juce_DeclareSingleton_SingleThreaded(classname, doNotRecreateAfterDeletion) \ \ static classname* _singletonInstance; \ \ static classname* getInstance() \ { \ if (_singletonInstance == nullptr) \ { \ static bool alreadyInside = false; \ static bool createdOnceAlready = false; \ \ const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ jassert (! problem); \ if (! problem) \ { \ createdOnceAlready = true; \ alreadyInside = true; \ classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ alreadyInside = false; \ \ _singletonInstance = newObject; \ } \ } \ \ return _singletonInstance; \ } \ \ static inline classname* getInstanceWithoutCreating() noexcept\ { \ return _singletonInstance; \ } \ \ static void deleteInstance() \ { \ if (_singletonInstance != nullptr) \ { \ classname* const old = _singletonInstance; \ _singletonInstance = nullptr; \ delete old; \ } \ } \ \ void clearSingletonInstance() noexcept\ { \ if (_singletonInstance == this) \ _singletonInstance = nullptr; \ } //============================================================================== /** Macro to declare member variables and methods for a singleton class. This is like juce_DeclareSingleton_SingleThreaded, but doesn't do any checking for recursion or repeated instantiation. It's intended for use as a lightweight version of a singleton, where you're using it in very straightforward circumstances and don't need the extra checking. Juce use the normal juce_ImplementSingleton_SingleThreaded as the counterpart to this declaration, as you would with juce_DeclareSingleton_SingleThreaded. See the documentation for juce_DeclareSingleton for more information about how to use it, the only difference being that you have to use juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton */ #define juce_DeclareSingleton_SingleThreaded_Minimal(classname) \ \ static classname* _singletonInstance; \ \ static classname* getInstance() \ { \ if (_singletonInstance == nullptr) \ _singletonInstance = new classname(); \ \ return _singletonInstance; \ } \ \ static inline classname* getInstanceWithoutCreating() noexcept\ { \ return _singletonInstance; \ } \ \ static void deleteInstance() \ { \ if (_singletonInstance != nullptr) \ { \ classname* const old = _singletonInstance; \ _singletonInstance = nullptr; \ delete old; \ } \ } \ \ void clearSingletonInstance() noexcept\ { \ if (_singletonInstance == this) \ _singletonInstance = nullptr; \ } //============================================================================== /** This is a counterpart to the juce_DeclareSingleton_SingleThreaded macro. After adding juce_DeclareSingleton_SingleThreaded or juce_DeclareSingleton_SingleThreaded_Minimal to the class definition, this macro has to be used somewhere in the cpp file. */ #define juce_ImplementSingleton_SingleThreaded(classname) \ \ classname* classname::_singletonInstance = nullptr; #endif // JUCE_SINGLETON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h000066400000000000000000000211511320201440200307370ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_WEAKREFERENCE_H_INCLUDED #define JUCE_WEAKREFERENCE_H_INCLUDED //============================================================================== /** This class acts as a pointer which will automatically become null if the object to which it points is deleted. To accomplish this, the source object needs to cooperate by performing a couple of simple tasks. It must embed a WeakReference::Master object, which stores a shared pointer object, and must clear this master pointer in its destructor. E.g. @code class MyObject { public: MyObject() { // If you're planning on using your WeakReferences in a multi-threaded situation, you may choose // to create a WeakReference to the object here in the constructor, which will pre-initialise the // embedded object, avoiding an (extremely unlikely) race condition that could occur if multiple // threads overlap while creating the first WeakReference to it. } ~MyObject() { // This will zero all the references - you need to call this in your destructor. masterReference.clear(); } private: // You need to embed a variable of this type, with the name "masterReference" inside your object. If the // variable is not public, you should make your class a friend of WeakReference so that the // WeakReference class can access it. WeakReference::Master masterReference; friend class WeakReference; }; // Here's an example of using a pointer.. MyObject* n = new MyObject(); WeakReference myObjectRef = n; MyObject* pointer1 = myObjectRef; // returns a valid pointer to 'n' delete n; MyObject* pointer2 = myObjectRef; // returns a null pointer @endcode @see WeakReference::Master */ template class WeakReference { public: /** Creates a null SafePointer. */ inline WeakReference() noexcept {} /** Creates a WeakReference that points at the given object. */ WeakReference (ObjectType* const object) : holder (getRef (object)) {} /** Creates a copy of another WeakReference. */ WeakReference (const WeakReference& other) noexcept : holder (other.holder) {} /** Copies another pointer to this one. */ WeakReference& operator= (const WeakReference& other) { holder = other.holder; return *this; } /** Copies another pointer to this one. */ WeakReference& operator= (ObjectType* const newObject) { holder = getRef (newObject); return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS WeakReference (WeakReference&& other) noexcept : holder (static_cast (other.holder)) {} WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast (other.holder); return *this; } #endif /** Returns the object that this pointer refers to, or null if the object no longer exists. */ ObjectType* get() const noexcept { return holder != nullptr ? holder->get() : nullptr; } /** Returns the object that this pointer refers to, or null if the object no longer exists. */ operator ObjectType*() const noexcept { return get(); } /** Returns the object that this pointer refers to, or null if the object no longer exists. */ ObjectType* operator->() noexcept { return get(); } /** Returns the object that this pointer refers to, or null if the object no longer exists. */ const ObjectType* operator->() const noexcept { return get(); } /** This returns true if this reference has been pointing at an object, but that object has since been deleted. If this reference was only ever pointing at a null pointer, this will return false. Using operator=() to make this refer to a different object will reset this flag to match the status of the reference from which you're copying. */ bool wasObjectDeleted() const noexcept { return holder != nullptr && holder->get() == nullptr; } bool operator== (ObjectType* const object) const noexcept { return get() == object; } bool operator!= (ObjectType* const object) const noexcept { return get() != object; } //============================================================================== /** This class is used internally by the WeakReference class - don't use it directly in your code! @see WeakReference */ class SharedPointer : public ReferenceCountingType { public: explicit SharedPointer (ObjectType* const obj) noexcept : owner (obj) {} inline ObjectType* get() const noexcept { return owner; } void clearPointer() noexcept { owner = nullptr; } private: ObjectType* volatile owner; JUCE_DECLARE_NON_COPYABLE (SharedPointer) }; typedef ReferenceCountedObjectPtr SharedRef; //============================================================================== /** This class is embedded inside an object to which you want to attach WeakReference pointers. See the WeakReference class notes for an example of how to use this class. @see WeakReference */ class Master { public: Master() noexcept {} ~Master() noexcept { // You must remember to call clear() in your source object's destructor! See the notes // for the WeakReference class for an example of how to do this. jassert (sharedPointer == nullptr || sharedPointer->get() == nullptr); } /** The first call to this method will create an internal object that is shared by all weak references to the object. */ SharedPointer* getSharedPointer (ObjectType* const object) { if (sharedPointer == nullptr) { sharedPointer = new SharedPointer (object); } else { // You're trying to create a weak reference to an object that has already been deleted!! jassert (sharedPointer->get() != nullptr); } return sharedPointer; } /** The object that owns this master pointer should call this before it gets destroyed, to zero all the references to this object that may be out there. See the WeakReference class notes for an example of how to do this. */ void clear() noexcept { if (sharedPointer != nullptr) sharedPointer->clearPointer(); } private: SharedRef sharedPointer; JUCE_DECLARE_NON_COPYABLE (Master) }; private: SharedRef holder; static inline SharedPointer* getRef (ObjectType* const o) { return (o != nullptr) ? o->masterReference.getSharedPointer (o) : nullptr; } }; #endif // JUCE_WEAKREFERENCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/misc/000077500000000000000000000000001320201440200244755ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp000066400000000000000000000054301320201440200274670ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ Result::Result() noexcept {} Result::Result (const String& message) noexcept : errorMessage (message) { } Result::Result (const Result& other) : errorMessage (other.errorMessage) { } Result& Result::operator= (const Result& other) { errorMessage = other.errorMessage; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Result::Result (Result&& other) noexcept : errorMessage (static_cast (other.errorMessage)) { } Result& Result::operator= (Result&& other) noexcept { errorMessage = static_cast (other.errorMessage); return *this; } #endif bool Result::operator== (const Result& other) const noexcept { return errorMessage == other.errorMessage; } bool Result::operator!= (const Result& other) const noexcept { return errorMessage != other.errorMessage; } Result Result::fail (const String& errorMessage) noexcept { return Result (errorMessage.isEmpty() ? "Unknown Error" : errorMessage); } const String& Result::getErrorMessage() const noexcept { return errorMessage; } bool Result::wasOk() const noexcept { return errorMessage.isEmpty(); } Result::operator bool() const noexcept { return errorMessage.isEmpty(); } bool Result::failed() const noexcept { return errorMessage.isNotEmpty(); } bool Result::operator!() const noexcept { return errorMessage.isNotEmpty(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/misc/juce_Result.h000066400000000000000000000105301320201440200271310ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_RESULT_H_INCLUDED #define JUCE_RESULT_H_INCLUDED //============================================================================== /** Represents the 'success' or 'failure' of an operation, and holds an associated error message to describe the error when there's a failure. E.g. @code Result myOperation() { if (doSomeKindOfFoobar()) return Result::ok(); else return Result::fail ("foobar didn't work!"); } const Result result (myOperation()); if (result.wasOk()) { ...it's all good... } else { warnUserAboutFailure ("The foobar operation failed! Error message was: " + result.getErrorMessage()); } @endcode */ class JUCE_API Result { public: //============================================================================== /** Creates and returns a 'successful' result. */ static Result ok() noexcept { return Result(); } /** Creates a 'failure' result. If you pass a blank error message in here, a default "Unknown Error" message will be used instead. */ static Result fail (const String& errorMessage) noexcept; //============================================================================== /** Returns true if this result indicates a success. */ bool wasOk() const noexcept; /** Returns true if this result indicates a failure. You can use getErrorMessage() to retrieve the error message associated with the failure. */ bool failed() const noexcept; /** Returns true if this result indicates a success. This is equivalent to calling wasOk(). */ operator bool() const noexcept; /** Returns true if this result indicates a failure. This is equivalent to calling failed(). */ bool operator!() const noexcept; /** Returns the error message that was set when this result was created. For a successful result, this will be an empty string; */ const String& getErrorMessage() const noexcept; //============================================================================== Result (const Result&); Result& operator= (const Result&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Result (Result&&) noexcept; Result& operator= (Result&&) noexcept; #endif bool operator== (const Result& other) const noexcept; bool operator!= (const Result& other) const noexcept; private: String errorMessage; // The default constructor is not for public use! // Instead, use Result::ok() or Result::fail() Result() noexcept; explicit Result (const String&) noexcept; // These casts are private to prevent people trying to use the Result object in numeric contexts operator int() const; operator void*() const; }; #endif // JUCE_RESULT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp000066400000000000000000000065561320201440200271310ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ Uuid::Uuid() { Random r; for (size_t i = 0; i < sizeof (uuid); ++i) uuid[i] = (uint8) (r.nextInt (256)); // To make it RFC 4122 compliant, need to force a few bits... uuid[6] = (uuid[6] & 0x0f) | 0x40; uuid[8] = (uuid[8] & 0x3f) | 0x80; } Uuid::~Uuid() noexcept {} Uuid::Uuid (const Uuid& other) noexcept { memcpy (uuid, other.uuid, sizeof (uuid)); } Uuid& Uuid::operator= (const Uuid& other) noexcept { memcpy (uuid, other.uuid, sizeof (uuid)); return *this; } bool Uuid::operator== (const Uuid& other) const noexcept { return memcmp (uuid, other.uuid, sizeof (uuid)) == 0; } bool Uuid::operator!= (const Uuid& other) const noexcept { return ! operator== (other); } Uuid Uuid::null() noexcept { return Uuid ((const uint8*) nullptr); } bool Uuid::isNull() const noexcept { for (size_t i = 0; i < sizeof (uuid); ++i) if (uuid[i] != 0) return false; return true; } String Uuid::getHexRegion (int start, int length) const { return String::toHexString (uuid + start, length, 0); } String Uuid::toString() const { return getHexRegion (0, 16); } String Uuid::toDashedString() const { return getHexRegion (0, 4) + "-" + getHexRegion (4, 2) + "-" + getHexRegion (6, 2) + "-" + getHexRegion (8, 2) + "-" + getHexRegion (10, 6); } Uuid::Uuid (const String& uuidString) { operator= (uuidString); } Uuid& Uuid::operator= (const String& uuidString) { MemoryBlock mb; mb.loadFromHexString (uuidString); mb.ensureSize (sizeof (uuid), true); mb.copyTo (uuid, 0, sizeof (uuid)); return *this; } Uuid::Uuid (const uint8* const rawData) noexcept { operator= (rawData); } Uuid& Uuid::operator= (const uint8* const rawData) noexcept { if (rawData != nullptr) memcpy (uuid, rawData, sizeof (uuid)); else zeromem (uuid, sizeof (uuid)); return *this; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h000066400000000000000000000105551320201440200265700ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_UUID_H_INCLUDED #define JUCE_UUID_H_INCLUDED //============================================================================== /** A universally unique 128-bit identifier. This class generates very random unique numbers. It's vanishingly unlikely that two identical UUIDs would ever be created by chance. The values are formatted to meet the RFC 4122 version 4 standard. The class includes methods for saving the ID as a string or as raw binary data. */ class JUCE_API Uuid { public: //============================================================================== /** Creates a new unique ID, compliant with RFC 4122 version 4. */ Uuid(); /** Destructor. */ ~Uuid() noexcept; /** Creates a copy of another UUID. */ Uuid (const Uuid&) noexcept; /** Copies another UUID. */ Uuid& operator= (const Uuid&) noexcept; //============================================================================== /** Returns true if the ID is zero. */ bool isNull() const noexcept; /** Returns a null Uuid object. */ static Uuid null() noexcept; bool operator== (const Uuid&) const noexcept; bool operator!= (const Uuid&) const noexcept; //============================================================================== /** Returns a stringified version of this UUID. A Uuid object can later be reconstructed from this string using operator= or the constructor that takes a string parameter. @returns a 32 character hex string. */ String toString() const; /** Returns a stringified version of this UUID, separating it into sections with dashes. @returns a string in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx */ String toDashedString() const; /** Creates an ID from an encoded string version. @see toString */ Uuid (const String& uuidString); /** Copies from a stringified UUID. The string passed in should be one that was created with the toString() method. */ Uuid& operator= (const String& uuidString); //============================================================================== /** Returns a pointer to the internal binary representation of the ID. This is an array of 16 bytes. To reconstruct a Uuid from its data, use the constructor or operator= method that takes an array of uint8s. */ const uint8* getRawData() const noexcept { return uuid; } /** Creates a UUID from a 16-byte array. @see getRawData */ Uuid (const uint8* rawData) noexcept; /** Sets this UUID from 16-bytes of raw data. */ Uuid& operator= (const uint8* rawData) noexcept; private: //============================================================================== uint8 uuid[16]; String getHexRegion (int, int) const; JUCE_LEAK_DETECTOR (Uuid) }; #endif // JUCE_UUID_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h000066400000000000000000000162321320201440200310430ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_WINDOWSREGISTRY_H_INCLUDED #define JUCE_WINDOWSREGISTRY_H_INCLUDED #if JUCE_WINDOWS || DOXYGEN /** Contains some static helper functions for manipulating the MS Windows registry (Only available on Windows, of course!) */ class JUCE_API WindowsRegistry { public: /** These values can be used to specify whether the 32- or 64-bit registry should be used. When running on a 32-bit OS, there is no 64-bit registry, so the mode will be ignored. */ enum WoW64Mode { /** Default handling: 32-bit apps will use the 32-bit registry, and 64-bit apps will use the 64-bit registry. */ WoW64_Default = 0, /** Always use the 64-bit registry store. (KEY_WOW64_64KEY). */ WoW64_64bit = 0x100, /** Always use the 32-bit registry store. (KEY_WOW64_32KEY). */ WoW64_32bit = 0x200 }; //============================================================================== /** Returns a string from the registry. The path is a string for the entire path of a value in the registry, e.g. "HKEY_CURRENT_USER\Software\foo\bar" */ static String JUCE_CALLTYPE getValue (const String& regValuePath, const String& defaultValue = String::empty, WoW64Mode mode = WoW64_Default); /** Reads a binary block from the registry. The path is a string for the entire path of a value in the registry, e.g. "HKEY_CURRENT_USER\Software\foo\bar" @returns a DWORD indicating the type of the key. */ static uint32 JUCE_CALLTYPE getBinaryValue (const String& regValuePath, MemoryBlock& resultData, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a string. This will take care of creating any groups needed to get to the given registry value. */ static bool JUCE_CALLTYPE setValue (const String& regValuePath, const String& value, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a DWORD. This will take care of creating any groups needed to get to the given registry value. */ static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint32 value, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a QWORD. This will take care of creating any groups needed to get to the given registry value. */ static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint64 value, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a binary block. This will take care of creating any groups needed to get to the given registry value. */ static bool JUCE_CALLTYPE setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode = WoW64_Default); /** Returns true if the given value exists in the registry. */ static bool JUCE_CALLTYPE valueExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); /** Returns true if the given key exists in the registry. */ static bool JUCE_CALLTYPE keyExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); /** Deletes a registry value. */ static void JUCE_CALLTYPE deleteValue (const String& regValuePath, WoW64Mode mode = WoW64_Default); /** Deletes a registry key (which is registry-talk for 'folder'). */ static void JUCE_CALLTYPE deleteKey (const String& regKeyPath, WoW64Mode mode = WoW64_Default); /** Creates a file association in the registry. This lets you set the executable that should be launched by a given file extension. @param fileExtension the file extension to associate, including the initial dot, e.g. ".txt" @param symbolicDescription a space-free short token to identify the file type @param fullDescription a human-readable description of the file type @param targetExecutable the executable that should be launched @param iconResourceNumber the icon that gets displayed for the file type will be found by looking up this resource number in the executable. Pass 0 here to not use an icon @param registerForCurrentUserOnly if false, this will try to register the association for all users (you might not have permission to do this unless running in an installer). If true, it will register the association in HKEY_CURRENT_USER. @param mode the WoW64 mode to use for choosing the database */ static bool JUCE_CALLTYPE registerFileAssociation (const String& fileExtension, const String& symbolicDescription, const String& fullDescription, const File& targetExecutable, int iconResourceNumber, bool registerForCurrentUserOnly, WoW64Mode mode = WoW64_Default); // DEPRECATED: use the other methods with a WoW64Mode parameter of WoW64_64bit instead. JUCE_DEPRECATED (static String getValueWow64 (const String&, const String& defaultValue = String::empty)); JUCE_DEPRECATED (static bool valueExistsWow64 (const String&)); JUCE_DEPRECATED (static bool keyExistsWow64 (const String&)); private: WindowsRegistry() JUCE_DELETED_FUNCTION; JUCE_DECLARE_NON_COPYABLE (WindowsRegistry) }; #endif #endif // JUCE_WINDOWSREGISTRY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/000077500000000000000000000000001320201440200250305ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/java/000077500000000000000000000000001320201440200257515ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/java/JuceAppActivity.java000066400000000000000000000764571320201440200317030ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ package com.juce; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; import android.view.*; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.graphics.*; import android.opengl.*; import android.text.ClipboardManager; import android.text.InputType; import android.util.DisplayMetrics; import android.util.Log; import java.io.*; import java.net.URL; import java.net.HttpURLConnection; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; //============================================================================== public class JuceAppActivity extends Activity { //============================================================================== static { System.loadLibrary ("juce_jni"); } @Override public void onCreate (Bundle savedInstanceState) { super.onCreate (savedInstanceState); viewHolder = new ViewHolder (this); setContentView (viewHolder); setVolumeControlStream (AudioManager.STREAM_MUSIC); } @Override protected void onDestroy() { quitApp(); super.onDestroy(); clearDataCache(); } @Override protected void onPause() { if (viewHolder != null) viewHolder.onPause(); suspendApp(); super.onPause(); } @Override protected void onResume() { super.onResume(); if (viewHolder != null) viewHolder.onResume(); resumeApp(); } @Override public void onConfigurationChanged (Configuration cfg) { super.onConfigurationChanged (cfg); setContentView (viewHolder); } private void callAppLauncher() { launchApp (getApplicationInfo().publicSourceDir, getApplicationInfo().dataDir); } //============================================================================== private native void launchApp (String appFile, String appDataDir); private native void quitApp(); private native void suspendApp(); private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); //============================================================================== public native void deliverMessage (long value); private android.os.Handler messageHandler = new android.os.Handler(); public final void postMessage (long value) { messageHandler.post (new MessageCallback (value)); } private final class MessageCallback implements Runnable { public MessageCallback (long value_) { value = value_; } public final void run() { deliverMessage (value); } private long value; } //============================================================================== private ViewHolder viewHolder; public final ComponentPeerView createNewView (boolean opaque, long host) { ComponentPeerView v = new ComponentPeerView (this, opaque, host); viewHolder.addView (v); return v; } public final void deleteView (ComponentPeerView view) { ViewGroup group = (ViewGroup) (view.getParent()); if (group != null) group.removeView (view); } public final void deleteOpenGLView (OpenGLView view) { ViewGroup group = (ViewGroup) (view.getParent()); if (group != null) group.removeView (view); } final class ViewHolder extends ViewGroup { public ViewHolder (Context context) { super (context); setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS); setFocusable (false); } protected final void onLayout (boolean changed, int left, int top, int right, int bottom) { setScreenSize (getWidth(), getHeight(), getDPI()); if (isFirstResize) { isFirstResize = false; callAppLauncher(); } } public final void onPause() { for (int i = getChildCount(); --i >= 0;) { View v = getChildAt (i); if (v instanceof ComponentPeerView) ((ComponentPeerView) v).onPause(); } } public final void onResume() { for (int i = getChildCount(); --i >= 0;) { View v = getChildAt (i); if (v instanceof ComponentPeerView) ((ComponentPeerView) v).onResume(); } } private final int getDPI() { DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics (metrics); return metrics.densityDpi; } private boolean isFirstResize = true; } public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom) { canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE); } //============================================================================== public final String getClipboardContent() { ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); return clipboard.getText().toString(); } public final void setClipboardContent (String newText) { ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); clipboard.setText (newText); } //============================================================================== public final void showMessageBox (String title, String message, final long callback) { AlertDialog.Builder builder = new AlertDialog.Builder (this); builder.setTitle (title) .setMessage (message) .setCancelable (true) .setPositiveButton ("OK", new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int id) { dialog.cancel(); JuceAppActivity.this.alertDismissed (callback, 0); } }); builder.create().show(); } public final void showOkCancelBox (String title, String message, final long callback) { AlertDialog.Builder builder = new AlertDialog.Builder (this); builder.setTitle (title) .setMessage (message) .setCancelable (true) .setPositiveButton ("OK", new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int id) { dialog.cancel(); JuceAppActivity.this.alertDismissed (callback, 1); } }) .setNegativeButton ("Cancel", new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int id) { dialog.cancel(); JuceAppActivity.this.alertDismissed (callback, 0); } }); builder.create().show(); } public final void showYesNoCancelBox (String title, String message, final long callback) { AlertDialog.Builder builder = new AlertDialog.Builder (this); builder.setTitle (title) .setMessage (message) .setCancelable (true) .setPositiveButton ("Yes", new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int id) { dialog.cancel(); JuceAppActivity.this.alertDismissed (callback, 1); } }) .setNegativeButton ("No", new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int id) { dialog.cancel(); JuceAppActivity.this.alertDismissed (callback, 2); } }) .setNeutralButton ("Cancel", new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int id) { dialog.cancel(); JuceAppActivity.this.alertDismissed (callback, 0); } }); builder.create().show(); } public native void alertDismissed (long callback, int id); //============================================================================== public final class ComponentPeerView extends ViewGroup implements View.OnFocusChangeListener { public ComponentPeerView (Context context, boolean opaque_, long host) { super (context); this.host = host; setWillNotDraw (false); opaque = opaque_; setFocusable (true); setFocusableInTouchMode (true); setOnFocusChangeListener (this); requestFocus(); // swap red and blue colours to match internal opengl texture format ColorMatrix colorMatrix = new ColorMatrix(); float[] colorTransform = { 0, 0, 1.0f, 0, 0, 0, 1.0f, 0, 0, 0, 1.0f, 0, 0, 0, 0, 0, 0, 0, 1.0f, 0 }; colorMatrix.set (colorTransform); paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix)); } //============================================================================== private native void handlePaint (long host, Canvas canvas, Paint paint); @Override public void onDraw (Canvas canvas) { handlePaint (host, canvas, paint); } @Override public boolean isOpaque() { return opaque; } private boolean opaque; private long host; private Paint paint = new Paint(); //============================================================================== private native void handleMouseDown (long host, int index, float x, float y, long time); private native void handleMouseDrag (long host, int index, float x, float y, long time); private native void handleMouseUp (long host, int index, float x, float y, long time); @Override public boolean onTouchEvent (MotionEvent event) { int action = event.getAction(); long time = event.getEventTime(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time); return true; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time); return true; case MotionEvent.ACTION_MOVE: { int n = event.getPointerCount(); for (int i = 0; i < n; ++i) handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time); return true; } case MotionEvent.ACTION_POINTER_UP: { int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time); return true; } case MotionEvent.ACTION_POINTER_DOWN: { int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time); return true; } default: break; } return false; } //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); public void showKeyboard (String type) { InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE); if (imm != null) { if (type.length() > 0) { imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT); imm.setInputMethod (getWindowToken(), type); } else { imm.hideSoftInputFromWindow (getWindowToken(), 0); } } } @Override public boolean onKeyDown (int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); default: break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); return true; } @Override public boolean onKeyUp (int keyCode, KeyEvent event) { handleKeyUp (host, keyCode, event.getUnicodeChar()); return true; } @Override public boolean onKeyMultiple (int keyCode, int count, KeyEvent event) { if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE) return super.onKeyMultiple (keyCode, count, event); if (event.getCharacters() != null) { int utf8Char = event.getCharacters().codePointAt (0); handleKeyDown (host, utf8Char, utf8Char); return true; } return false; } // this is here to make keyboard entry work on a Galaxy Tab2 10.1 @Override public InputConnection onCreateInputConnection (EditorInfo outAttrs) { outAttrs.actionLabel = ""; outAttrs.hintText = ""; outAttrs.initialCapsMode = 0; outAttrs.initialSelEnd = outAttrs.initialSelStart = -1; outAttrs.label = ""; outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI; outAttrs.inputType = InputType.TYPE_NULL; return new BaseInputConnection (this, false); } //============================================================================== @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { super.onSizeChanged (w, h, oldw, oldh); viewSizeChanged (host); } @Override protected void onLayout (boolean changed, int left, int top, int right, int bottom) { for (int i = getChildCount(); --i >= 0;) requestTransparentRegion (getChildAt (i)); } private native void viewSizeChanged (long host); @Override public void onFocusChange (View v, boolean hasFocus) { if (v == this) focusChanged (host, hasFocus); } private native void focusChanged (long host, boolean hasFocus); public void setViewName (String newName) {} public boolean isVisible() { return getVisibility() == VISIBLE; } public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); } public boolean containsPoint (int x, int y) { return true; //xxx needs to check overlapping views } public final void onPause() { for (int i = getChildCount(); --i >= 0;) { View v = getChildAt (i); if (v instanceof OpenGLView) ((OpenGLView) v).onPause(); } } public final void onResume() { for (int i = getChildCount(); --i >= 0;) { View v = getChildAt (i); if (v instanceof OpenGLView) ((OpenGLView) v).onResume(); } } public OpenGLView createGLView() { OpenGLView glView = new OpenGLView (getContext()); addView (glView); return glView; } } //============================================================================== public final class OpenGLView extends GLSurfaceView implements GLSurfaceView.Renderer { OpenGLView (Context context) { super (context); setEGLContextClientVersion (2); setRenderer (this); setRenderMode (RENDERMODE_WHEN_DIRTY); } @Override public void onSurfaceCreated (GL10 unused, EGLConfig config) { contextCreated(); } @Override public void onSurfaceChanged (GL10 unused, int width, int height) { contextChangedSize(); } @Override public void onDrawFrame (GL10 unused) { render(); } private native void contextCreated(); private native void contextChangedSize(); private native void render(); } //============================================================================== public final int[] renderGlyph (char glyph, Paint paint, android.graphics.Matrix matrix, Rect bounds) { Path p = new Path(); paint.getTextPath (String.valueOf (glyph), 0, 1, 0.0f, 0.0f, p); RectF boundsF = new RectF(); p.computeBounds (boundsF, true); matrix.mapRect (boundsF); boundsF.roundOut (bounds); bounds.left--; bounds.right++; final int w = bounds.width(); final int h = Math.max (1, bounds.height()); Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas (bm); matrix.postTranslate (-bounds.left, -bounds.top); c.setMatrix (matrix); c.drawPath (p, paint); final int sizeNeeded = w * h; if (cachedRenderArray.length < sizeNeeded) cachedRenderArray = new int [sizeNeeded]; bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h); bm.recycle(); return cachedRenderArray; } private int[] cachedRenderArray = new int [256]; //============================================================================== public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, int[] statusCode, StringBuffer responseHeaders) throws IOException { connection = connection_; try { inputStream = new BufferedInputStream (connection.getInputStream()); } catch (IOException e) { if (connection.getResponseCode() < org.apache.http.HttpStatus.SC_BAD_REQUEST) throw e; } finally { statusCode[0] = connection.getResponseCode(); } if (statusCode[0] >= org.apache.http.HttpStatus.SC_BAD_REQUEST) inputStream = connection.getErrorStream(); else inputStream = connection.getInputStream(); for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) if (entry.getKey() != null && entry.getValue() != null) responseHeaders.append (entry.getKey() + ": " + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { try { inputStream.close(); } catch (IOException e) {} connection.disconnect(); } public final int read (byte[] buffer, int numBytes) { int num = 0; try { num = inputStream.read (buffer, 0, numBytes); } catch (IOException e) {} if (num > 0) position += num; return num; } public final long getPosition() { return position; } public final long getTotalLength() { return -1; } public final boolean isExhausted() { return false; } public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; private InputStream inputStream; private long position; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, String headers, int timeOutMs, int[] statusCode, StringBuffer responseHeaders, int numRedirectsToFollow) { // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL) if (timeOutMs < 0) timeOutMs = 0; 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); // 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); } } if (isPost) { connection.setRequestMethod ("POST"); 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(); } } } catch (Throwable e) {} return null; } } public final void launchURL (String url) { startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url))); } public static final String getLocaleValue (boolean isRegion) { java.util.Locale locale = java.util.Locale.getDefault(); return isRegion ? locale.getDisplayCountry (java.util.Locale.US) : locale.getDisplayLanguage (java.util.Locale.US); } //============================================================================== private final class SingleMediaScanner implements MediaScannerConnectionClient { public SingleMediaScanner (Context context, String filename) { file = filename; msc = new MediaScannerConnection (context, this); msc.connect(); } @Override public void onMediaScannerConnected() { msc.scanFile (file, null); } @Override public void onScanCompleted (String path, Uri uri) { msc.disconnect(); } private MediaScannerConnection msc; private String file; } public final void scanFile (String filename) { new SingleMediaScanner (this, filename); } public final Typeface getTypeFaceFromAsset (String assetName) { try { return Typeface.createFromAsset (this.getResources().getAssets(), assetName); } catch (Throwable e) {} return null; } final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); public static String bytesToHex (byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; ++j) { int v = bytes[j] & 0xff; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0f]; } return new String (hexChars); } final private java.util.Map dataCache = new java.util.HashMap(); synchronized private final File getDataCacheFile (byte[] data) { try { java.security.MessageDigest digest = java.security.MessageDigest.getInstance ("MD5"); digest.update (data); String key = bytesToHex (digest.digest()); if (dataCache.containsKey (key)) return (File) dataCache.get (key); File f = new File (this.getCacheDir(), "bindata_" + key); f.delete(); FileOutputStream os = new FileOutputStream (f); os.write (data, 0, data.length); dataCache.put (key, f); return f; } catch (Throwable e) {} return null; } private final void clearDataCache() { java.util.Iterator it = dataCache.values().iterator(); while (it.hasNext()) { File f = (File) it.next(); f.delete(); } } public final Typeface getTypeFaceFromByteArray (byte[] data) { try { File f = getDataCacheFile (data); if (f != null) return Typeface.createFromFile (f); } catch (Exception e) { Log.e ("JUCE", e.toString()); } return null; } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h000066400000000000000000000147471320201440200317100ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_BASICNATIVEHEADERS_H_INCLUDED #define JUCE_BASICNATIVEHEADERS_H_INCLUDED #include "../system/juce_TargetPlatform.h" #undef T //============================================================================== #if JUCE_MAC || JUCE_IOS #if JUCE_IOS #import #import #import #import #include #else #define Point CarbonDummyPointName #define Component CarbonDummyCompName #import #import #undef Point #undef Component #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================== #elif JUCE_WINDOWS #if JUCE_MSVC #ifndef _CPPRTTI #error "You're compiling without RTTI enabled! This is needed for a lot of JUCE classes, please update your compiler settings!" #endif #ifndef _CPPUNWIND #error "You're compiling without exceptions enabled! This is needed for a lot of JUCE classes, please update your compiler settings!" #endif #pragma warning (push) #pragma warning (disable : 4100 4201 4514 4312 4995) #endif #define STRICT 1 #define WIN32_LEAN_AND_MEAN 1 #if JUCE_MINGW #define _WIN32_WINNT 0x0501 #else #define _WIN32_WINNT 0x0600 #endif #define _UNICODE 1 #define UNICODE 1 #ifndef _WIN32_IE #define _WIN32_IE 0x0500 #endif #include #include #include #include #include #include #include #if JUCE_MINGW #include #endif #include #include #include #include #include #include #include #if JUCE_MINGW #include #else #include #include #endif #undef PACKED #if JUCE_MSVC #pragma warning (pop) #pragma warning (4: 4511 4512 4100 /*4365*/) // (enable some warnings that are turned off in VC8) #endif #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment (lib, "kernel32.lib") #pragma comment (lib, "user32.lib") #pragma comment (lib, "wininet.lib") #pragma comment (lib, "advapi32.lib") #pragma comment (lib, "ws2_32.lib") #pragma comment (lib, "version.lib") #pragma comment (lib, "shlwapi.lib") #pragma comment (lib, "winmm.lib") #ifdef _NATIVE_WCHAR_T_DEFINED #ifdef _DEBUG #pragma comment (lib, "comsuppwd.lib") #else #pragma comment (lib, "comsuppw.lib") #endif #else #ifdef _DEBUG #pragma comment (lib, "comsuppd.lib") #else #pragma comment (lib, "comsupp.lib") #endif #endif #endif /* Used with DynamicLibrary to simplify importing functions from a win32 DLL. dll: the DynamicLibrary object functionName: function to import localFunctionName: name you want to use to actually call it (must be different) returnType: the return type params: list of params (bracketed) */ #define JUCE_LOAD_WINAPI_FUNCTION(dll, functionName, localFunctionName, returnType, params) \ typedef returnType (WINAPI *type##localFunctionName) params; \ type##localFunctionName localFunctionName = (type##localFunctionName) dll.getFunction (#functionName); //============================================================================== #elif JUCE_LINUX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================== #elif JUCE_ANDROID #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // Need to clear various moronic redefinitions made by system headers.. #undef max #undef min #undef direct #undef check #endif // JUCE_BASICNATIVEHEADERS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp000066400000000000000000000060501320201440200313050ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ bool File::isOnCDRomDrive() const { return false; } bool File::isOnHardDisk() const { return true; } bool File::isOnRemovableDrive() const { return false; } String File::getVersion() const { return String::empty; } File File::getSpecialLocation (const SpecialLocationType type) { switch (type) { case userHomeDirectory: case userDocumentsDirectory: case userMusicDirectory: case userMoviesDirectory: case userPicturesDirectory: case userApplicationDataDirectory: case userDesktopDirectory: return File (android.appDataDir); case commonApplicationDataDirectory: case commonDocumentsDirectory: return File (android.appDataDir); case globalApplicationsDirectory: return File ("/system/app"); case tempDirectory: return File (android.appDataDir).getChildFile (".temp"); case invokedExecutableFile: case currentExecutableFile: case currentApplicationFile: case hostApplicationPath: return juce_getExecutableFile(); default: jassertfalse; // unknown type? break; } return File(); } bool File::moveToTrash() const { if (! exists()) return true; // TODO return false; } JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { const LocalRef t (javaString (fileName)); android.activity.callVoidMethod (JuceAppActivity.launchURL, t.get()); return true; } void File::revealToUser() const { } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h000066400000000000000000000376531320201440200316700ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_ANDROID_JNIHELPERS_H_INCLUDED #define JUCE_ANDROID_JNIHELPERS_H_INCLUDED #if ! (defined (JUCE_ANDROID_ACTIVITY_CLASSNAME) && defined (JUCE_ANDROID_ACTIVITY_CLASSPATH)) #error "The JUCE_ANDROID_ACTIVITY_CLASSNAME and JUCE_ANDROID_ACTIVITY_CLASSPATH macros must be set!" #endif //============================================================================== extern JNIEnv* getEnv() noexcept; //============================================================================== class GlobalRef { public: inline GlobalRef() noexcept : obj (0) {} inline explicit GlobalRef (jobject o) : obj (retain (o)) {} inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {} ~GlobalRef() { clear(); } inline void clear() { if (obj != 0) { getEnv()->DeleteGlobalRef (obj); obj = 0; } } inline GlobalRef& operator= (const GlobalRef& other) { jobject newObj = retain (other.obj); clear(); obj = newObj; return *this; } //============================================================================== inline operator jobject() const noexcept { return obj; } inline jobject get() const noexcept { return obj; } //============================================================================== #define DECLARE_CALL_TYPE_METHOD(returnType, typeName) \ returnType call##typeName##Method (jmethodID methodID, ... ) const \ { \ va_list args; \ va_start (args, methodID); \ returnType result = getEnv()->Call##typeName##MethodV (obj, methodID, args); \ va_end (args); \ return result; \ } DECLARE_CALL_TYPE_METHOD (jobject, Object) DECLARE_CALL_TYPE_METHOD (jboolean, Boolean) DECLARE_CALL_TYPE_METHOD (jbyte, Byte) DECLARE_CALL_TYPE_METHOD (jchar, Char) DECLARE_CALL_TYPE_METHOD (jshort, Short) DECLARE_CALL_TYPE_METHOD (jint, Int) DECLARE_CALL_TYPE_METHOD (jlong, Long) DECLARE_CALL_TYPE_METHOD (jfloat, Float) DECLARE_CALL_TYPE_METHOD (jdouble, Double) #undef DECLARE_CALL_TYPE_METHOD void callVoidMethod (jmethodID methodID, ... ) const { va_list args; va_start (args, methodID); getEnv()->CallVoidMethodV (obj, methodID, args); va_end (args); } private: //============================================================================== jobject obj; static inline jobject retain (jobject obj) { return obj == 0 ? 0 : getEnv()->NewGlobalRef (obj); } }; //============================================================================== template class LocalRef { public: explicit inline LocalRef (JavaType o) noexcept : obj (o) {} inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {} ~LocalRef() { clear(); } void clear() { if (obj != 0) getEnv()->DeleteLocalRef (obj); } LocalRef& operator= (const LocalRef& other) { jobject newObj = retain (other.obj); clear(); obj = newObj; return *this; } inline operator JavaType() const noexcept { return obj; } inline JavaType get() const noexcept { return obj; } private: JavaType obj; static JavaType retain (JavaType obj) { return obj == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj); } }; //============================================================================== namespace { String juceString (JNIEnv* env, jstring s) { const char* const utf8 = env->GetStringUTFChars (s, nullptr); CharPointer_UTF8 utf8CP (utf8); const String result (utf8CP); env->ReleaseStringUTFChars (s, utf8); return result; } String juceString (jstring s) { return juceString (getEnv(), s); } LocalRef javaString (const String& s) { return LocalRef (getEnv()->NewStringUTF (s.toUTF8())); } LocalRef javaStringFromChar (const juce_wchar c) { char utf8[8] = { 0 }; CharPointer_UTF8 (utf8).write (c); return LocalRef (getEnv()->NewStringUTF (utf8)); } } //============================================================================== class JNIClassBase { public: explicit JNIClassBase (const char* classPath); virtual ~JNIClassBase(); inline operator jclass() const noexcept { return classRef; } static void initialiseAllClasses (JNIEnv*); static void releaseAllClasses (JNIEnv*); protected: virtual void initialiseFields (JNIEnv*) = 0; jmethodID resolveMethod (JNIEnv*, const char* methodName, const char* params); jmethodID resolveStaticMethod (JNIEnv*, const char* methodName, const char* params); jfieldID resolveField (JNIEnv*, const char* fieldName, const char* signature); jfieldID resolveStaticField (JNIEnv*, const char* fieldName, const char* signature); private: const char* const classPath; jclass classRef; static Array& getClasses(); void initialise (JNIEnv*); void release (JNIEnv*); JUCE_DECLARE_NON_COPYABLE (JNIClassBase) }; //============================================================================== #define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params); #define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params); #define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature); #define CREATE_JNI_STATICFIELD(fieldID, stringName, signature) fieldID = resolveStaticField (env, stringName, signature); #define DECLARE_JNI_METHOD(methodID, stringName, params) jmethodID methodID; #define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID; #define DECLARE_JNI_CLASS(CppClassName, javaPath) \ class CppClassName ## _Class : public JNIClassBase \ { \ public: \ CppClassName ## _Class() : JNIClassBase (javaPath) {} \ \ void initialiseFields (JNIEnv* env) \ { \ JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD); \ } \ \ JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD); \ }; \ static CppClassName ## _Class CppClassName; //============================================================================== #if defined (__arm__) #define JUCE_ARM_SOFT_FLOAT_ABI __attribute__ ((pcs("aapcs"))) #else #define JUCE_ARM_SOFT_FLOAT_ABI #endif #define JUCE_JNI_CALLBACK(className, methodName, returnType, params) \ extern "C" __attribute__ ((visibility("default"))) JUCE_ARM_SOFT_FLOAT_ABI returnType JUCE_JOIN_MACRO (JUCE_JOIN_MACRO (Java_, className), _ ## methodName) params //============================================================================== class AndroidSystem { public: AndroidSystem(); void initialise (JNIEnv*, jobject activity, jstring appFile, jstring appDataDir); void shutdown (JNIEnv*); //============================================================================== GlobalRef activity; String appFile, appDataDir; int screenWidth, screenHeight, dpi; }; extern AndroidSystem android; //============================================================================== class ThreadLocalJNIEnvHolder { public: ThreadLocalJNIEnvHolder() noexcept : jvm (nullptr) { zeromem (threads, sizeof (threads)); zeromem (envs, sizeof (envs)); } void initialise (JNIEnv* env) { // NB: the DLL can be left loaded by the JVM, so the same static // objects can end up being reused by subsequent runs of the app zeromem (threads, sizeof (threads)); zeromem (envs, sizeof (envs)); env->GetJavaVM (&jvm); addEnv (env); } JNIEnv* attach() noexcept { if (android.activity != nullptr) { if (JNIEnv* env = attachToCurrentThread()) { SpinLock::ScopedLockType sl (addRemoveLock); return addEnv (env); } jassertfalse; } return nullptr; } void detach() noexcept { if (android.activity != nullptr) { jvm->DetachCurrentThread(); removeCurrentThreadFromCache(); } } void removeCurrentThreadFromCache() { const pthread_t thisThread = pthread_self(); SpinLock::ScopedLockType sl (addRemoveLock); for (int i = 0; i < maxThreads; ++i) { if (threads[i] == thisThread) { threads[i] = 0; envs[i] = nullptr; } } } JNIEnv* getOrAttach() noexcept { if (JNIEnv* env = get()) return env; SpinLock::ScopedLockType sl (addRemoveLock); if (JNIEnv* env = get()) return env; if (JNIEnv* env = attachToCurrentThread()) return addEnv (env); return nullptr; } private: JavaVM* jvm; enum { maxThreads = 32 }; pthread_t threads [maxThreads]; JNIEnv* envs [maxThreads]; SpinLock addRemoveLock; JNIEnv* addEnv (JNIEnv* env) noexcept { const pthread_t thisThread = pthread_self(); for (int i = 0; i < maxThreads; ++i) { if (threads[i] == 0) { envs[i] = env; threads[i] = thisThread; return env; } } jassertfalse; // too many threads! return nullptr; } JNIEnv* get() const noexcept { const pthread_t thisThread = pthread_self(); for (int i = 0; i < maxThreads; ++i) if (threads[i] == thisThread) return envs[i]; return nullptr; } JNIEnv* attachToCurrentThread() { JNIEnv* env = nullptr; jvm->AttachCurrentThread (&env, nullptr); return env; } }; extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; struct AndroidThreadScope { AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); } ~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); } }; //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ METHOD (deleteOpenGLView, "deleteOpenGLView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView;)V") \ METHOD (postMessage, "postMessage", "(J)V") \ METHOD (finish, "finish", "()V") \ METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \ METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;I)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \ METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") \ METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ METHOD (getTypeFaceFromByteArray,"getTypeFaceFromByteArray","([B)Landroid/graphics/Typeface;") DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH); #undef JNI_CLASS_MEMBERS //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (constructor, "", "(I)V") \ METHOD (setColor, "setColor", "(I)V") \ METHOD (setAlpha, "setAlpha", "(I)V") \ METHOD (setTypeface, "setTypeface", "(Landroid/graphics/Typeface;)Landroid/graphics/Typeface;") \ METHOD (ascent, "ascent", "()F") \ METHOD (descent, "descent", "()F") \ METHOD (setTextSize, "setTextSize", "(F)V") \ METHOD (getTextWidths, "getTextWidths", "(Ljava/lang/String;[F)I") \ METHOD (setTextScaleX, "setTextScaleX", "(F)V") \ METHOD (getTextPath, "getTextPath", "(Ljava/lang/String;IIFFLandroid/graphics/Path;)V") \ METHOD (setShader, "setShader", "(Landroid/graphics/Shader;)Landroid/graphics/Shader;") \ DECLARE_JNI_CLASS (Paint, "android/graphics/Paint"); #undef JNI_CLASS_MEMBERS //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (constructor, "", "()V") \ METHOD (setValues, "setValues", "([F)V") \ DECLARE_JNI_CLASS (Matrix, "android/graphics/Matrix"); #undef JNI_CLASS_MEMBERS //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (constructor, "", "(IIII)V") \ FIELD (left, "left", "I") \ FIELD (right, "right", "I") \ FIELD (top, "top", "I") \ FIELD (bottom, "bottom", "I") \ DECLARE_JNI_CLASS (RectClass, "android/graphics/Rect"); #undef JNI_CLASS_MEMBERS #endif // JUCE_ANDROID_JNIHELPERS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp000066400000000000000000000030231320201440200311330ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ void Logger::outputDebugString (const String& text) { __android_log_print (ANDROID_LOG_INFO, "JUCE", "%s", text.toUTF8().getAddress()); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp000066400000000000000000000176051320201440200317040ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (constructor, "", "()V") \ METHOD (toString, "toString", "()Ljava/lang/String;") \ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer"); #undef JNI_CLASS_MEMBERS //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (release, "release", "()V") \ METHOD (read, "read", "([BI)I") \ METHOD (getPosition, "getPosition", "()J") \ METHOD (getTotalLength, "getTotalLength", "()J") \ METHOD (isExhausted, "isExhausted", "()Z") \ METHOD (setPosition, "setPosition", "(J)Z") \ DECLARE_JNI_CLASS (HTTPStream, JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream"); #undef JNI_CLASS_MEMBERS //============================================================================== void MACAddress::findAllAddresses (Array& result) { // TODO } JUCE_API bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, const String& emailSubject, const String& bodyText, const StringArray& filesToAttach) { // TODO return false; } //============================================================================== class WebInputStream : public InputStream { public: WebInputStream (String address, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers, int timeOutMs, StringPairArray* responseHeaders, const int numRedirectsToFollow) : statusCode (0) { if (! address.contains ("://")) address = "http://" + address; JNIEnv* env = getEnv(); jbyteArray postDataArray = 0; if (postData.getSize() > 0) { postDataArray = env->NewByteArray (postData.getSize()); env->SetByteArrayRegion (postDataArray, 0, postData.getSize(), (const jbyte*) postData.getData()); } LocalRef responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); // Annoyingly, the android HTTP functions will choke on this call if you try to do it on the message // thread. You'll need to move your networking code to a background thread to keep it happy.. jassert (Thread::getCurrentThread() != nullptr); jintArray statusCodeArray = env->NewIntArray (1); jassert (statusCodeArray != 0); stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, JuceAppActivity.createHTTPStream, javaString (address).get(), (jboolean) isPost, postDataArray, javaString (headers).get(), (jint) timeOutMs, statusCodeArray, responseHeaderBuffer.get(), (jint) numRedirectsToFollow)); jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); statusCode = statusCodeElements[0]; env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); env->DeleteLocalRef (statusCodeArray); if (postDataArray != 0) env->DeleteLocalRef (postDataArray); if (stream != 0) { StringArray headerLines; { LocalRef headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(), StringBuffer.toString)); headerLines.addLines (juceString (env, headersString)); } if (responseHeaders != 0) { for (int i = 0; i < headerLines.size(); ++i) { const String& header = headerLines[i]; const String key (header.upToFirstOccurrenceOf (": ", false, false)); const String value (header.fromFirstOccurrenceOf (": ", false, false)); const String previousValue ((*responseHeaders) [key]); responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } } } } ~WebInputStream() { if (stream != 0) stream.callVoidMethod (HTTPStream.release); } //============================================================================== bool isError() const { return stream == nullptr; } bool isExhausted() override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } int64 getTotalLength() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } int64 getPosition() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } bool setPosition (int64 wantedPos) override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); } int read (void* buffer, int bytesToRead) override { jassert (buffer != nullptr && bytesToRead >= 0); if (stream == nullptr) return 0; JNIEnv* env = getEnv(); jbyteArray javaArray = env->NewByteArray (bytesToRead); int numBytes = stream.callIntMethod (HTTPStream.read, javaArray, (jint) bytesToRead); if (numBytes > 0) env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast (buffer)); env->DeleteLocalRef (javaArray); return numBytes; } //============================================================================== GlobalRef stream; int statusCode; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp000066400000000000000000000220611320201440200325460ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0) { getClasses().add (this); } JNIClassBase::~JNIClassBase() { getClasses().removeFirstMatchingValue (this); } Array& JNIClassBase::getClasses() { static Array classes; return classes; } void JNIClassBase::initialise (JNIEnv* env) { classRef = (jclass) env->NewGlobalRef (env->FindClass (classPath)); jassert (classRef != 0); initialiseFields (env); } void JNIClassBase::release (JNIEnv* env) { env->DeleteGlobalRef (classRef); } void JNIClassBase::initialiseAllClasses (JNIEnv* env) { const Array& classes = getClasses(); for (int i = classes.size(); --i >= 0;) classes.getUnchecked(i)->initialise (env); } void JNIClassBase::releaseAllClasses (JNIEnv* env) { const Array& classes = getClasses(); for (int i = classes.size(); --i >= 0;) classes.getUnchecked(i)->release (env); } jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params) { jmethodID m = env->GetMethodID (classRef, methodName, params); jassert (m != 0); return m; } jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params) { jmethodID m = env->GetStaticMethodID (classRef, methodName, params); jassert (m != 0); return m; } jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature) { jfieldID f = env->GetFieldID (classRef, fieldName, signature); jassert (f != 0); return f; } jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature) { jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature); jassert (f != 0); return f; } //============================================================================== ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; #if JUCE_DEBUG static bool systemInitialised = false; #endif JNIEnv* getEnv() noexcept { #if JUCE_DEBUG if (! systemInitialised) { DBG ("*** Call to getEnv() when system not initialised"); jassertfalse; std::exit (EXIT_FAILURE); } #endif return threadLocalJNIEnvHolder.getOrAttach(); } extern "C" jint JNI_OnLoad (JavaVM*, void*) { return JNI_VERSION_1_2; } //============================================================================== AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160) { } void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir) { screenWidth = screenHeight = 0; dpi = 160; JNIClassBase::initialiseAllClasses (env); threadLocalJNIEnvHolder.initialise (env); #if JUCE_DEBUG systemInitialised = true; #endif activity = GlobalRef (act); appFile = juceString (env, file); appDataDir = juceString (env, dataDir); } void AndroidSystem::shutdown (JNIEnv* env) { activity.clear(); #if JUCE_DEBUG systemInitialised = false; #endif JNIClassBase::releaseAllClasses (env); } AndroidSystem android; //============================================================================== namespace AndroidStatsHelpers { #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;") DECLARE_JNI_CLASS (SystemClass, "java/lang/System"); #undef JNI_CLASS_MEMBERS String getSystemProperty (const String& name) { return juceString (LocalRef ((jstring) getEnv()->CallStaticObjectMethod (SystemClass, SystemClass.getProperty, javaString (name).get()))); } String getLocaleValue (bool isRegion) { return juceString (LocalRef ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, JuceAppActivity.getLocaleValue, isRegion))); } #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) DECLARE_JNI_CLASS (BuildClass, "android/os/Build"); #undef JNI_CLASS_MEMBERS String getAndroidOsBuildValue (const char* fieldName) { return juceString (LocalRef ((jstring) getEnv()->GetStaticObjectField ( BuildClass, getEnv()->GetStaticFieldID (BuildClass, fieldName, "Ljava/lang/String;")))); } } //============================================================================== SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { return Android; } String SystemStats::getOperatingSystemName() { return "Android " + AndroidStatsHelpers::getSystemProperty ("os.version"); } String SystemStats::getDeviceDescription() { return AndroidStatsHelpers::getAndroidOsBuildValue ("MODEL") + "-" + AndroidStatsHelpers::getAndroidOsBuildValue ("SERIAL"); } bool SystemStats::isOperatingSystem64Bit() { #if JUCE_64BIT return true; #else return false; #endif } String SystemStats::getCpuVendor() { return AndroidStatsHelpers::getSystemProperty ("os.arch"); } int SystemStats::getCpuSpeedInMegaherz() { return 0; // TODO } int SystemStats::getMemorySizeInMegabytes() { #if __ANDROID_API__ >= 9 struct sysinfo sysi; if (sysinfo (&sysi) == 0) return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); #endif return 0; } int SystemStats::getPageSize() { return sysconf (_SC_PAGESIZE); } //============================================================================== String SystemStats::getLogonName() { if (const char* user = getenv ("USER")) return CharPointer_UTF8 (user); if (struct passwd* const pw = getpwuid (getuid())) return CharPointer_UTF8 (pw->pw_name); return String::empty; } String SystemStats::getFullUserName() { return getLogonName(); } String SystemStats::getComputerName() { char name [256] = { 0 }; if (gethostname (name, sizeof (name) - 1) == 0) return name; return String::empty; } String SystemStats::getUserLanguage() { return AndroidStatsHelpers::getLocaleValue (false); } String SystemStats::getUserRegion() { return AndroidStatsHelpers::getLocaleValue (true); } String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } //============================================================================== void CPUInformation::initialise() noexcept { numCpus = jmax ((int) 1, (int) sysconf (_SC_NPROCESSORS_ONLN)); } //============================================================================== uint32 juce_millisecondsSinceStartup() noexcept { timespec t; clock_gettime (CLOCK_MONOTONIC, &t); return t.tv_sec * 1000 + t.tv_nsec / 1000000; } int64 Time::getHighResolutionTicks() noexcept { timespec t; clock_gettime (CLOCK_MONOTONIC, &t); return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); } int64 Time::getHighResolutionTicksPerSecond() noexcept { return 1000000; // (microseconds) } double Time::getMillisecondCounterHiRes() noexcept { return getHighResolutionTicks() * 0.001; } bool Time::setSystemTimeToThisTime() const { jassertfalse; return false; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp000066400000000000000000000053151320201440200316400ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ /* Note that a lot of methods that you'd expect to find in this file actually live in juce_posix_SharedCode.h! */ //============================================================================== // sets the process to 0=low priority, 1=normal, 2=high, 3=realtime JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) { // TODO struct sched_param param; int policy, maxp, minp; const int p = (int) prior; if (p <= 1) policy = SCHED_OTHER; else policy = SCHED_RR; minp = sched_get_priority_min (policy); maxp = sched_get_priority_max (policy); if (p < 2) param.sched_priority = 0; else if (p == 2 ) // Set to middle of lower realtime priority range param.sched_priority = minp + (maxp - minp) / 4; else // Set to middle of higher realtime priority range param.sched_priority = minp + (3 * (maxp - minp) / 4); pthread_setschedparam (pthread_self(), policy, ¶m); } JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() { return false; } JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() { return juce_isRunningUnderDebugger(); } JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp000066400000000000000000000363201320201440200312240ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ class WebInputStream : public InputStream { public: WebInputStream (const String& address, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers, int timeOutMs, StringPairArray* responseHeaders, const int maxRedirects) : multi (nullptr), curl (nullptr), headerList (nullptr), lastError (CURLE_OK), contentLength (-1), streamPos (0), finished (false), skipBytes (0), postBuffer (nullptr), postPosition (0) { statusCode = -1; if (init() && setOptions (address, timeOutMs, (responseHeaders != nullptr), maxRedirects, headers, isPost, postData.getSize())) { connect (responseHeaders, isPost, postData, progressCallback, progressCallbackContext); } else { cleanup(); } } ~WebInputStream() { cleanup(); } //============================================================================== // Input Stream overrides bool isError() const { return curl == nullptr || lastError != CURLE_OK; } bool isExhausted() override { return (isError() || finished) && curlBuffer.getSize() == 0; } int64 getPosition() override { return streamPos; } int64 getTotalLength() override { return contentLength; } int read (void* buffer, int bytesToRead) override { return readOrSkip (buffer, bytesToRead, false); } bool setPosition (int64 wantedPos) override { const int amountToSkip = static_cast (wantedPos - getPosition()); if (amountToSkip < 0) return false; if (amountToSkip == 0) return true; const int actuallySkipped = readOrSkip (nullptr, amountToSkip, true); return actuallySkipped == amountToSkip; } //============================================================================== int statusCode; private: //============================================================================== bool init() { multi = curl_multi_init(); if (multi != nullptr) { curl = curl_easy_init(); if (curl != nullptr) if (curl_multi_add_handle (multi, curl) == CURLM_OK) return true; } cleanup(); return false; } void cleanup() { if (curl != nullptr) { curl_multi_remove_handle (multi, curl); if (headerList != nullptr) { curl_slist_free_all (headerList); headerList = nullptr; } curl_easy_cleanup (curl); curl = nullptr; } if (multi != nullptr) { curl_multi_cleanup (multi); multi = nullptr; } } //============================================================================== bool setOptions (const String& address, int timeOutMs, bool wantsHeaders, const int maxRedirects, const String& headers, bool isPost, size_t postSize) { if (curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK && curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK && curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK && curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast (maxRedirects)) == CURLE_OK) { if (isPost) { if (curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK || curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK) return false; if (curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK || curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast (postSize)) != CURLE_OK) return false; } // do we want to parse the headers if (wantsHeaders) { if (curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK || curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK) return false; } if (headers.isNotEmpty()) { const StringArray headerLines = StringArray::fromLines (headers); // fromLines will always return at least one line if the string is not empty jassert (headerLines.size() > 0); headerList = curl_slist_append (headerList, headerLines [0].toRawUTF8()); for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i) headerList = curl_slist_append (headerList, headerLines [i].toRawUTF8()); if (headerList == nullptr) return false; if (curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK) return false; } if (timeOutMs > 0) { long timeOutSecs = static_cast (ceil (static_cast (timeOutMs) / 1000.0)); if (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK) return false; } return true; } return false; } void connect (StringPairArray* responseHeaders, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { if (isPost) postBuffer = &postData; size_t lastPos = static_cast (-1); // step until either: 1) there is an error 2) the transaction is complete // or 3) data is in the in buffer while ((! finished) && curlBuffer.getSize() == 0 && curl != nullptr) { singleStep(); // call callbacks if this is a post request if (isPost && progressCallback != nullptr && lastPos != postPosition) { lastPos = postPosition; if (! progressCallback (progressCallbackContext, static_cast (lastPos), static_cast (postData.getSize()))) { // user has decided to abort the transaction cleanup(); return; } } } long responseCode; if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK) statusCode = static_cast (responseCode); // parse headers if (responseHeaders != nullptr) parseHttpHeaders (*responseHeaders); // get content length size double curlLength; if (curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK) contentLength = static_cast (curlLength); } void finish() { if (curl == nullptr) return; for (;;) { int cnt = 0; if (CURLMsg* msg = curl_multi_info_read (multi, &cnt)) { if (msg->msg == CURLMSG_DONE && msg->easy_handle == curl) { lastError = msg->data.result; // this is the error that stopped our process from continuing break; } } else { break; } } finished = true; } //============================================================================== void singleStep() { if (curl == nullptr || lastError != CURLE_OK) return; fd_set fdread, fdwrite, fdexcep; int maxfd = -1; long curl_timeo; if ((lastError = (int) curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK) return; // why 980? see http://curl.haxx.se/libcurl/c/curl_multi_timeout.html if (curl_timeo < 0) curl_timeo = 980; struct timeval tv; tv.tv_sec = curl_timeo / 1000; tv.tv_usec = (curl_timeo % 1000) * 1000; FD_ZERO (&fdread); FD_ZERO (&fdwrite); FD_ZERO (&fdexcep); if ((lastError = (int) curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK) return; if (maxfd != -1) { if (select (maxfd + 1, &fdread, &fdwrite, &fdexcep, &tv) < 0) { lastError = -1; return; } } else { // if curl does not return any sockets for to wait on, then the doc says to wait 100 ms Thread::sleep (100); } int still_running = 0; int curlRet; while ((curlRet = (int) curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM) {} if ((lastError = curlRet) != CURLM_OK) return; if (still_running <= 0) finish(); } int readOrSkip (void* buffer, int bytesToRead, bool skip) { if (bytesToRead <= 0) return 0; size_t pos = 0; size_t len = static_cast (bytesToRead); while (len > 0) { size_t bufferBytes = curlBuffer.getSize(); bool removeSection = true; if (bufferBytes == 0) { // do not call curl again if we are finished if (finished || curl == nullptr) return static_cast (pos); skipBytes = skip ? len : 0; singleStep(); // update the amount that was read/skipped from curl bufferBytes = skip ? len - skipBytes : curlBuffer.getSize(); removeSection = ! skip; } // can we copy data from the internal buffer? if (bufferBytes > 0) { size_t max = jmin (len, bufferBytes); if (! skip) memcpy (addBytesToPointer (buffer, pos), curlBuffer.getData(), max); pos += max; streamPos += static_cast (max); len -= max; if (removeSection) curlBuffer.removeSection (0, max); } } return static_cast (pos); } //============================================================================== void parseHttpHeaders (StringPairArray& responseHeaders) { StringArray headerLines = StringArray::fromLines (curlHeaders); // ignore the first line as this is the status line for (int i = 1; i < headerLines.size(); ++i) { const String& headersEntry = headerLines[i]; if (headersEntry.isNotEmpty()) { const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); const String previousValue (responseHeaders [key]); responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } } } //============================================================================== // CURL callbacks size_t curlWriteCallback (char* ptr, size_t size, size_t nmemb) { if (curl == nullptr || lastError != CURLE_OK) return 0; const size_t len = size * nmemb; // skip bytes if necessary size_t max = jmin (skipBytes, len); skipBytes -= max; if (len > max) curlBuffer.append (ptr + max, len - max); return len; } size_t curlReadCallback (char* ptr, size_t size, size_t nmemb) { if (curl == nullptr || postBuffer == nullptr || lastError != CURLE_OK) return 0; const size_t len = size * nmemb; size_t max = jmin (postBuffer->getSize() - postPosition, len); memcpy (ptr, (char*)postBuffer->getData() + postPosition, max); postPosition += max; return max; } size_t curlHeaderCallback (char* ptr, size_t size, size_t nmemb) { if (curl == nullptr || lastError != CURLE_OK) return 0; size_t len = size * nmemb; curlHeaders += String (ptr, len); return len; } //============================================================================== // Static method wrappers static size_t StaticCurlWrite (char* ptr, size_t size, size_t nmemb, void* userdata) { WebInputStream* wi = reinterpret_cast (userdata); return wi->curlWriteCallback (ptr, size, nmemb); } static size_t StaticCurlRead (char* ptr, size_t size, size_t nmemb, void* userdata) { WebInputStream* wi = reinterpret_cast (userdata); return wi->curlReadCallback (ptr, size, nmemb); } static size_t StaticCurlHeader (char* ptr, size_t size, size_t nmemb, void* userdata) { WebInputStream* wi = reinterpret_cast (userdata); return wi->curlHeaderCallback (ptr, size, nmemb); } private: CURLM* multi; CURL* curl; struct curl_slist* headerList; int lastError; //============================================================================== // internal buffers and buffer positions int64 contentLength, streamPos; MemoryBlock curlBuffer; String curlHeaders; bool finished; size_t skipBytes; //============================================================================== // Http POST variables const MemoryBlock* postBuffer; size_t postPosition; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp000066400000000000000000000112621320201440200320130ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ bool File::copyInternal (const File& dest) const { FileInputStream in (*this); if (dest.deleteFile()) { { FileOutputStream out (dest); if (out.failedToOpen()) return false; if (out.writeFromInputStream (in, -1) == getSize()) return true; } dest.deleteFile(); } return false; } void File::findFileSystemRoots (Array& destArray) { destArray.add (File ("/")); } bool File::isHidden() const { return getFileName().startsWithChar ('.'); } static String getLinkedFile (StringRef file) { HeapBlock buffer (8194); const int numBytes = (int) readlink (file.text, buffer, 8192); return String::fromUTF8 (buffer, jmax (0, numBytes)); }; bool File::isLink() const { return getLinkedFile (getFullPathName()).isNotEmpty(); } File File::getLinkedTarget() const { String f (getLinkedFile (getFullPathName())); if (f.isNotEmpty()) return getSiblingFile (f); return *this; } //============================================================================== class DirectoryIterator::NativeIterator::Pimpl { public: Pimpl (const File& directory, const String& wc) : parentDir (File::addTrailingSeparator (directory.getFullPathName())), wildCard (wc), dir (opendir (directory.getFullPathName().toUTF8())) { } ~Pimpl() { if (dir != nullptr) closedir (dir); } bool next (String& filenameFound, bool* const isDir, bool* const isHidden, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { if (dir != nullptr) { const char* wildcardUTF8 = nullptr; for (;;) { struct dirent* const de = readdir (dir); if (de == nullptr) break; if (wildcardUTF8 == nullptr) wildcardUTF8 = wildCard.toUTF8(); if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) { filenameFound = CharPointer_UTF8 (de->d_name); updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); if (isHidden != nullptr) *isHidden = filenameFound.startsWithChar ('.'); return true; } } } return false; } private: String parentDir, wildCard; DIR* dir; JUCE_DECLARE_NON_COPYABLE (Pimpl) }; DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCardStr) : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCardStr)) { } DirectoryIterator::NativeIterator::~NativeIterator() {} bool DirectoryIterator::NativeIterator::next (String& filenameFound, bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) { return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp000066400000000000000000000177171320201440200310400ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ enum { U_ISOFS_SUPER_MAGIC = 0x9660, // linux/iso_fs.h U_MSDOS_SUPER_MAGIC = 0x4d44, // linux/msdos_fs.h U_NFS_SUPER_MAGIC = 0x6969, // linux/nfs_fs.h U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h }; bool File::isOnCDRomDrive() const { struct statfs buf; return statfs (getFullPathName().toUTF8(), &buf) == 0 && buf.f_type == (short) U_ISOFS_SUPER_MAGIC; } bool File::isOnHardDisk() const { struct statfs buf; if (statfs (getFullPathName().toUTF8(), &buf) == 0) { switch (buf.f_type) { case U_ISOFS_SUPER_MAGIC: // CD-ROM case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem) case U_NFS_SUPER_MAGIC: // Network NFS case U_SMB_SUPER_MAGIC: // Network Samba return false; default: break; } } // Assume so if this fails for some reason return true; } bool File::isOnRemovableDrive() const { jassertfalse; // xxx not implemented for linux! return false; } String File::getVersion() const { return String(); // xxx not yet implemented } //============================================================================== static File resolveXDGFolder (const char* const type, const char* const fallbackFolder) { StringArray confLines; File ("~/.config/user-dirs.dirs").readLines (confLines); for (int i = 0; i < confLines.size(); ++i) { const String line (confLines[i].trimStart()); if (line.startsWith (type)) { // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music const File f (line.replace ("$HOME", File ("~").getFullPathName()) .fromFirstOccurrenceOf ("=", false, false) .trim().unquoted()); if (f.isDirectory()) return f; } } return File (fallbackFolder); } const char* const* juce_argv = nullptr; int juce_argc = 0; File File::getSpecialLocation (const SpecialLocationType type) { switch (type) { case userHomeDirectory: { if (const char* homeDir = getenv ("HOME")) return File (CharPointer_UTF8 (homeDir)); if (struct passwd* const pw = getpwuid (getuid())) return File (CharPointer_UTF8 (pw->pw_dir)); return File(); } case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~"); case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~"); case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~"); case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~"); case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop"); case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~"); case commonDocumentsDirectory: case commonApplicationDataDirectory: return File ("/var"); case globalApplicationsDirectory: return File ("/usr"); case tempDirectory: { File tmp ("/var/tmp"); if (! tmp.isDirectory()) { tmp = "/tmp"; if (! tmp.isDirectory()) tmp = File::getCurrentWorkingDirectory(); } return tmp; } case invokedExecutableFile: if (juce_argv != nullptr && juce_argc > 0) return File (CharPointer_UTF8 (juce_argv[0])); // deliberate fall-through... case currentExecutableFile: case currentApplicationFile: #if ! JUCE_STANDALONE_APPLICATION return juce_getExecutableFile(); #endif // deliberate fall-through if this is not a shared-library case hostApplicationPath: { const File f ("/proc/self/exe"); return f.isLink() ? f.getLinkedTarget() : juce_getExecutableFile(); } default: jassertfalse; // unknown type? break; } return File(); } //============================================================================== bool File::moveToTrash() const { if (! exists()) return true; File trashCan ("~/.Trash"); if (! trashCan.isDirectory()) trashCan = "~/.local/share/Trash/files"; if (! trashCan.isDirectory()) return false; return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(), getFileExtension())); } //============================================================================== static bool isFileExecutable (const String& filename) { juce_statStruct info; return juce_stat (filename, info) && S_ISREG (info.st_mode) && access (filename.toUTF8(), X_OK) == 0; } bool Process::openDocument (const String& fileName, const String& parameters) { String cmdString (fileName.replace (" ", "\\ ",false)); cmdString << " " << parameters; if (URL::isProbablyAWebsiteURL (fileName) || cmdString.startsWithIgnoreCase ("file:") || URL::isProbablyAnEmailAddress (fileName) || File::createFileWithoutCheckingPath (fileName).isDirectory() || ! isFileExecutable (fileName)) { // create a command that tries to launch a bunch of likely browsers static const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", "google-chrome", "chromium-browser", "opera", "konqueror" }; StringArray cmdLines; for (int i = 0; i < numElementsInArray (browserNames); ++i) cmdLines.add (String (browserNames[i]) + " " + cmdString.trim().quoted()); cmdString = cmdLines.joinIntoString (" || "); } const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 }; const int cpid = fork(); if (cpid == 0) { setsid(); // Child process execve (argv[0], (char**) argv, environ); exit (0); } return cpid >= 0; } void File::revealToUser() const { if (isDirectory()) startAsProcess(); else if (getParentDirectory().exists()) getParentDirectory().startAsProcess(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp000066400000000000000000000431641320201440200314220ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ void MACAddress::findAllAddresses (Array& result) { const int s = socket (AF_INET, SOCK_DGRAM, 0); if (s != -1) { struct ifaddrs* addrs = nullptr; if (getifaddrs (&addrs) != -1) { for (struct ifaddrs* i = addrs; i != nullptr; i = i->ifa_next) { struct ifreq ifr; strcpy (ifr.ifr_name, i->ifa_name); ifr.ifr_addr.sa_family = AF_INET; if (ioctl (s, SIOCGIFHWADDR, &ifr) == 0) { MACAddress ma ((const uint8*) ifr.ifr_hwaddr.sa_data); if (! ma.isNull()) result.addIfNotAlreadyThere (ma); } } freeifaddrs (addrs); } close (s); } } bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEmailAddress */, const String& /* emailSubject */, const String& /* bodyText */, const StringArray& /* filesToAttach */) { jassertfalse; // xxx todo return false; } //============================================================================== #if ! JUCE_USE_CURL class WebInputStream : public InputStream { public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, const int maxRedirects) : statusCode (0), socketHandle (-1), levelsOfRedirection (0), address (address_), headers (headers_), postData (postData_), contentLength (-1), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (maxRedirects), chunkEnd (0), isChunked (false), readingChunk (false) { statusCode = createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); if (responseHeaders != nullptr && ! isError()) { for (int i = 0; i < headerLines.size(); ++i) { const String& headersEntry = headerLines[i]; const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); const String previousValue ((*responseHeaders) [key]); responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } } } ~WebInputStream() { closeSocket(); } //============================================================================== bool isError() const { return socketHandle < 0; } bool isExhausted() override { return finished; } int64 getPosition() override { return position; } int64 getTotalLength() override { return contentLength; } int read (void* buffer, int bytesToRead) override { if (finished || isError()) return 0; if (isChunked && ! readingChunk) { if (position >= chunkEnd) { const ScopedValueSetter setter (readingChunk, true, false); MemoryOutputStream chunkLengthBuffer; char c = 0; if (chunkEnd > 0) { if (read (&c, 1) != 1 || c != '\r' || read (&c, 1) != 1 || c != '\n') { finished = true; return 0; } } while (chunkLengthBuffer.getDataSize() < 512 && ! (finished || isError())) { if (read (&c, 1) != 1) { finished = true; return 0; } if (c == '\r') continue; if (c == '\n') break; chunkLengthBuffer.writeByte (c); } const int64 chunkSize = chunkLengthBuffer.toString().trimStart().getHexValue64(); if (chunkSize == 0) { finished = true; return 0; } chunkEnd += chunkSize; } if (bytesToRead > chunkEnd - position) bytesToRead = chunkEnd - position; } fd_set readbits; FD_ZERO (&readbits); FD_SET (socketHandle, &readbits); struct timeval tv; tv.tv_sec = jmax (1, timeOutMs / 1000); tv.tv_usec = 0; if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) return 0; // (timeout) const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, (size_t) bytesToRead, MSG_WAITALL)); if (bytesRead == 0) finished = true; if (! readingChunk) position += bytesRead; return bytesRead; } bool setPosition (int64 wantedPos) override { if (isError()) return false; if (wantedPos != position) { finished = false; if (wantedPos < position) { closeSocket(); position = 0; statusCode = createConnection (0, 0, numRedirectsToFollow); } skipNextBytes (wantedPos - position); } return true; } //============================================================================== int statusCode; private: int socketHandle, levelsOfRedirection; StringArray headerLines; String address, headers; MemoryBlock postData; int64 contentLength, position; bool finished; const bool isPost; const int timeOutMs; const int numRedirectsToFollow; int64 chunkEnd; bool isChunked, readingChunk; void closeSocket (bool resetLevelsOfRedirection = true) { if (socketHandle >= 0) close (socketHandle); socketHandle = -1; if (resetLevelsOfRedirection) levelsOfRedirection = 0; } int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const int numRedirects) { closeSocket (false); uint32 timeOutTime = Time::getMillisecondCounter(); if (timeOutMs == 0) timeOutTime += 30000; else if (timeOutMs < 0) timeOutTime = 0xffffffff; else timeOutTime += (uint32) timeOutMs; String hostName, hostPath; int hostPort; if (! decomposeURL (address, hostName, hostPath, hostPort)) return 0; String serverName, proxyName, proxyPath; int proxyPort = 0; int port = 0; const String proxyURL (getenv ("http_proxy")); if (proxyURL.startsWithIgnoreCase ("http://")) { if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) return 0; serverName = proxyName; port = proxyPort; } else { serverName = hostName; port = hostPort; } struct addrinfo hints; zerostruct (hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; struct addrinfo* result = nullptr; if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) return 0; socketHandle = socket (result->ai_family, result->ai_socktype, 0); if (socketHandle == -1) { freeaddrinfo (result); return 0; } int receiveBufferSize = 16384; setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); #if JUCE_MAC setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); #endif if (connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1) { closeSocket(); freeaddrinfo (result); return 0; } freeaddrinfo (result); { const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, hostPath, address, headers, postData, isPost)); if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext)) { closeSocket(); return 0; } } String responseHeader (readResponse (timeOutTime)); position = 0; if (responseHeader.isNotEmpty()) { headerLines = StringArray::fromLines (responseHeader); const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false) .substring (0, 3).getIntValue(); String location (findHeaderItem (headerLines, "Location:")); if (++levelsOfRedirection <= numRedirects && status >= 300 && status < 400 && location.isNotEmpty() && location != address) { if (! (location.startsWithIgnoreCase ("http://") || location.startsWithIgnoreCase ("https://") || location.startsWithIgnoreCase ("ftp://"))) { // The following is a bit dodgy. Ideally, we should do a proper transform of the relative URI to a target URI if (location.startsWithChar ('/')) location = URL (address).withNewSubPath (location).toString (true); else location = address + "/" + location; } address = location; return createConnection (progressCallback, progressCallbackContext, numRedirects); } String contentLengthString (findHeaderItem (headerLines, "Content-Length:")); if (contentLengthString.isNotEmpty()) contentLength = contentLengthString.getLargeIntValue(); isChunked = (findHeaderItem (headerLines, "Transfer-Encoding:") == "chunked"); return status; } closeSocket(); return 0; } //============================================================================== String readResponse (const uint32 timeOutTime) { int numConsecutiveLFs = 0; MemoryOutputStream buffer; while (numConsecutiveLFs < 2 && buffer.getDataSize() < 32768 && Time::getMillisecondCounter() <= timeOutTime && ! (finished || isError())) { char c = 0; if (read (&c, 1) != 1) return String(); buffer.writeByte (c); if (c == '\n') ++numConsecutiveLFs; else if (c != '\r') numConsecutiveLFs = 0; } const String header (buffer.toString().trimEnd()); if (header.startsWithIgnoreCase ("HTTP/")) return header; return String(); } static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value) { if (! headers.containsIgnoreCase (key)) dest << "\r\n" << key << ' ' << value; } static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, int port) { dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.1\r\nHost: " << host; /* HTTP spec 14.23 says that the port number must be included in the header if it is not 80 */ if (port != 80) dest << ':' << port; } static MemoryBlock createRequestHeader (const String& hostName, const int hostPort, const String& proxyName, const int proxyPort, const String& hostPath, const String& originalURL, const String& userHeaders, const MemoryBlock& postData, const bool isPost) { MemoryOutputStream header; if (proxyName.isEmpty()) writeHost (header, isPost, hostPath, hostName, hostPort); else writeHost (header, isPost, originalURL, proxyName, proxyPort); writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) "." JUCE_STRINGIFY(JUCE_MINOR_VERSION) "." JUCE_STRINGIFY(JUCE_BUILDNUMBER)); writeValueIfNotPresent (header, userHeaders, "Connection:", "close"); if (isPost) { writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); header << userHeaders << "\r\n" << postData; } else { header << "\r\n" << userHeaders << "\r\n"; } return header.getMemoryBlock(); } static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { size_t totalHeaderSent = 0; while (totalHeaderSent < requestHeader.getSize()) { if (Time::getMillisecondCounter() > timeOutTime) return false; const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend) return false; totalHeaderSent += (size_t) numToSend; if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, (int) totalHeaderSent, (int) requestHeader.getSize())) return false; } return true; } static bool decomposeURL (const String& url, String& host, String& path, int& port) { if (! url.startsWithIgnoreCase ("http://")) return false; const int nextSlash = url.indexOfChar (7, '/'); int nextColon = url.indexOfChar (7, ':'); if (nextColon > nextSlash && nextSlash > 0) nextColon = -1; if (nextColon >= 0) { host = url.substring (7, nextColon); if (nextSlash >= 0) port = url.substring (nextColon + 1, nextSlash).getIntValue(); else port = url.substring (nextColon + 1).getIntValue(); } else { port = 80; if (nextSlash >= 0) host = url.substring (7, nextSlash); else host = url.substring (7); } if (nextSlash >= 0) path = url.substring (nextSlash); else path = "/"; return true; } static String findHeaderItem (const StringArray& lines, const String& itemName) { for (int i = 0; i < lines.size(); ++i) if (lines[i].startsWithIgnoreCase (itemName)) return lines[i].substring (itemName.length()).trim(); return String(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp000066400000000000000000000140141320201440200322640ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ void Logger::outputDebugString (const String& text) { std::cerr << text << std::endl; } //============================================================================== SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { return Linux; } String SystemStats::getOperatingSystemName() { return "Linux"; } bool SystemStats::isOperatingSystem64Bit() { #if JUCE_64BIT return true; #else //xxx not sure how to find this out?.. return false; #endif } //============================================================================== namespace LinuxStatsHelpers { String getConfigFileValue (const char* file, const char* const key) { StringArray lines; File (file).readLines (lines); for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase (key)) return lines[i].fromFirstOccurrenceOf (":", false, false).trim(); return String(); } String getCpuInfo (const char* key) { return getConfigFileValue ("/proc/cpuinfo", key); } } String SystemStats::getDeviceDescription() { return LinuxStatsHelpers::getCpuInfo ("Hardware"); } String SystemStats::getCpuVendor() { String v (LinuxStatsHelpers::getCpuInfo ("vendor_id")); if (v.isEmpty()) v = LinuxStatsHelpers::getCpuInfo ("model name"); return v; } int SystemStats::getCpuSpeedInMegaherz() { return roundToInt (LinuxStatsHelpers::getCpuInfo ("cpu MHz").getFloatValue()); } int SystemStats::getMemorySizeInMegabytes() { struct sysinfo sysi; if (sysinfo (&sysi) == 0) return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024)); return 0; } int SystemStats::getPageSize() { return (int) sysconf (_SC_PAGESIZE); } //============================================================================== String SystemStats::getLogonName() { if (const char* user = getenv ("USER")) return CharPointer_UTF8 (user); if (struct passwd* const pw = getpwuid (getuid())) return CharPointer_UTF8 (pw->pw_name); return String(); } String SystemStats::getFullUserName() { return getLogonName(); } String SystemStats::getComputerName() { char name [256] = { 0 }; if (gethostname (name, sizeof (name) - 1) == 0) return name; return String(); } static String getLocaleValue (nl_item key) { const char* oldLocale = ::setlocale (LC_ALL, ""); String result (String::fromUTF8 (nl_langinfo (key))); ::setlocale (LC_ALL, oldLocale); return result; } String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); } String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); } String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } //============================================================================== void CPUInformation::initialise() noexcept { const String flags (LinuxStatsHelpers::getCpuInfo ("flags")); hasMMX = flags.contains ("mmx"); hasSSE = flags.contains ("sse"); hasSSE2 = flags.contains ("sse2"); hasSSE3 = flags.contains ("sse3"); has3DNow = flags.contains ("3dnow"); hasSSSE3 = flags.contains ("ssse3"); hasAVX = flags.contains ("avx"); numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; } //============================================================================== uint32 juce_millisecondsSinceStartup() noexcept { timespec t; clock_gettime (CLOCK_MONOTONIC, &t); return (uint32) (t.tv_sec * 1000 + t.tv_nsec / 1000000); } int64 Time::getHighResolutionTicks() noexcept { timespec t; clock_gettime (CLOCK_MONOTONIC, &t); return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); } int64 Time::getHighResolutionTicksPerSecond() noexcept { return 1000000; // (microseconds) } double Time::getMillisecondCounterHiRes() noexcept { return getHighResolutionTicks() * 0.001; } bool Time::setSystemTimeToThisTime() const { timeval t; t.tv_sec = millisSinceEpoch / 1000; t.tv_usec = (millisSinceEpoch - t.tv_sec * 1000) * 1000; return settimeofday (&t, 0) == 0; } JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() { #if JUCE_BSD return false; #else return LinuxStatsHelpers::getConfigFileValue ("/proc/self/status", "TracerPid") .getIntValue() > 0; #endif } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp000066400000000000000000000055401320201440200313570ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ /* Note that a lot of methods that you'd expect to find in this file actually live in juce_posix_SharedCode.h! */ //============================================================================== JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) { const int policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR; const int minp = sched_get_priority_min (policy); const int maxp = sched_get_priority_max (policy); struct sched_param param; switch (prior) { case LowPriority: case NormalPriority: param.sched_priority = 0; break; case HighPriority: param.sched_priority = minp + (maxp - minp) / 4; break; case RealtimePriority: param.sched_priority = minp + (3 * (maxp - minp) / 4); break; default: jassertfalse; break; } pthread_setschedparam (pthread_self(), policy, ¶m); } JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() { return juce_isRunningUnderDebugger(); } static bool swapUserAndEffectiveUser() { int result1 = setreuid (geteuid(), getuid()); int result2 = setregid (getegid(), getgid()); return result1 == 0 && result2 == 0; } JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); } JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() { if (geteuid() == 0 && getuid() != 0) swapUserAndEffectiveUser(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm000066400000000000000000000424141320201440200302600ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ /* Note that a lot of methods that you'd expect to find in this file actually live in juce_posix_SharedCode.h! */ //============================================================================== bool File::copyInternal (const File& dest) const { JUCE_AUTORELEASEPOOL { NSFileManager* fm = [NSFileManager defaultManager]; return [fm fileExistsAtPath: juceStringToNS (fullPath)] #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 && [fm copyItemAtPath: juceStringToNS (fullPath) toPath: juceStringToNS (dest.getFullPathName()) error: nil]; #else && [fm copyPath: juceStringToNS (fullPath) toPath: juceStringToNS (dest.getFullPathName()) handler: nil]; #endif } } void File::findFileSystemRoots (Array& destArray) { destArray.add (File ("/")); } //============================================================================== namespace FileHelpers { static bool isFileOnDriveType (const File& f, const char* const* types) { struct statfs buf; if (juce_doStatFS (f, buf)) { const String type (buf.f_fstypename); while (*types != 0) if (type.equalsIgnoreCase (*types++)) return true; } return false; } static bool isHiddenFile (const String& path) { #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 JUCE_AUTORELEASEPOOL { NSNumber* hidden = nil; NSError* err = nil; return [[NSURL fileURLWithPath: juceStringToNS (path)] getResourceValue: &hidden forKey: NSURLIsHiddenKey error: &err] && [hidden boolValue]; } #elif JUCE_IOS return File (path).getFileName().startsWithChar ('.'); #else FSRef ref; LSItemInfoRecord info; return FSPathMakeRefWithOptions ((const UInt8*) path.toRawUTF8(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr && LSCopyItemInfoForRef (&ref, kLSRequestBasicFlagsOnly, &info) == noErr && (info.flags & kLSItemInfoIsInvisible) != 0; #endif } #if JUCE_IOS static String getIOSSystemLocation (NSSearchPathDirectory type) { return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) objectAtIndex: 0]); } #endif static bool launchExecutable (const String& pathAndArguments) { const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), 0 }; const int cpid = fork(); if (cpid == 0) { // Child process if (execve (argv[0], (char**) argv, 0) < 0) exit (0); } else { if (cpid < 0) return false; } return true; } } bool File::isOnCDRomDrive() const { static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", nullptr }; return FileHelpers::isFileOnDriveType (*this, cdTypes); } bool File::isOnHardDisk() const { static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", nullptr }; return ! (isOnCDRomDrive() || FileHelpers::isFileOnDriveType (*this, nonHDTypes)); } bool File::isOnRemovableDrive() const { #if JUCE_IOS return false; // xxx is this possible? #else JUCE_AUTORELEASEPOOL { BOOL removable = false; [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: juceStringToNS (getFullPathName()) isRemovable: &removable isWritable: nil isUnmountable: nil description: nil type: nil]; return removable; } #endif } bool File::isHidden() const { return FileHelpers::isHiddenFile (getFullPathName()); } //============================================================================== const char* const* juce_argv = nullptr; int juce_argc = 0; File File::getSpecialLocation (const SpecialLocationType type) { JUCE_AUTORELEASEPOOL { String resultPath; switch (type) { case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; #if JUCE_IOS case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break; case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break; case tempDirectory: { File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory)); tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); tmp.createDirectory(); return tmp.getFullPathName(); } #else case userDocumentsDirectory: resultPath = "~/Documents"; break; case userDesktopDirectory: resultPath = "~/Desktop"; break; case tempDirectory: { File tmp ("~/Library/Caches/" + juce_getExecutableFile().getFileNameWithoutExtension()); tmp.createDirectory(); return File (tmp.getFullPathName()); } #endif case userMusicDirectory: resultPath = "~/Music"; break; case userMoviesDirectory: resultPath = "~/Movies"; break; case userPicturesDirectory: resultPath = "~/Pictures"; break; case userApplicationDataDirectory: resultPath = "~/Library"; break; case commonApplicationDataDirectory: resultPath = "/Library"; break; case commonDocumentsDirectory: resultPath = "/Users/Shared"; break; case globalApplicationsDirectory: resultPath = "/Applications"; break; case invokedExecutableFile: if (juce_argv != nullptr && juce_argc > 0) return File (CharPointer_UTF8 (juce_argv[0])); // deliberate fall-through... case currentExecutableFile: return juce_getExecutableFile(); case currentApplicationFile: { const File exe (juce_getExecutableFile()); const File parent (exe.getParentDirectory()); #if JUCE_IOS return parent; #else return parent.getFullPathName().endsWithIgnoreCase ("Contents/MacOS") ? parent.getParentDirectory().getParentDirectory() : exe; #endif } case hostApplicationPath: { unsigned int size = 8192; HeapBlock buffer; buffer.calloc (size + 8); _NSGetExecutablePath (buffer.getData(), &size); return File (String::fromUTF8 (buffer, (int) size)); } default: jassertfalse; // unknown type? break; } if (resultPath.isNotEmpty()) return File (resultPath.convertToPrecomposedUnicode()); } return File(); } //============================================================================== String File::getVersion() const { JUCE_AUTORELEASEPOOL { if (NSBundle* bundle = [NSBundle bundleWithPath: juceStringToNS (getFullPathName())]) if (NSDictionary* info = [bundle infoDictionary]) if (NSString* name = [info valueForKey: nsStringLiteral ("CFBundleShortVersionString")]) return nsStringToJuce (name); } return String(); } //============================================================================== static NSString* getFileLink (const String& path) { #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (path) error: nil]; #else // (the cast here avoids a deprecation warning) return [((id) [NSFileManager defaultManager]) pathContentOfSymbolicLinkAtPath: juceStringToNS (path)]; #endif } bool File::isLink() const { return getFileLink (fullPath) != nil; } File File::getLinkedTarget() const { if (NSString* dest = getFileLink (fullPath)) return getSiblingFile (nsStringToJuce (dest)); return *this; } //============================================================================== bool File::moveToTrash() const { if (! exists()) return true; #if JUCE_IOS return deleteFile(); //xxx is there a trashcan on the iOS? #else JUCE_AUTORELEASEPOOL { NSString* p = juceStringToNS (getFullPathName()); return [[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation source: [p stringByDeletingLastPathComponent] destination: nsEmptyString() files: [NSArray arrayWithObject: [p lastPathComponent]] tag: nil ]; } #endif } //============================================================================== class DirectoryIterator::NativeIterator::Pimpl { public: Pimpl (const File& directory, const String& wildCard_) : parentDir (File::addTrailingSeparator (directory.getFullPathName())), wildCard (wildCard_), enumerator (nil) { JUCE_AUTORELEASEPOOL { enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory.getFullPathName())] retain]; } } ~Pimpl() { [enumerator release]; } bool next (String& filenameFound, bool* const isDir, bool* const isHidden, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { JUCE_AUTORELEASEPOOL { const char* wildcardUTF8 = nullptr; for (;;) { NSString* file; if (enumerator == nil || (file = [enumerator nextObject]) == nil) return false; [enumerator skipDescendents]; filenameFound = nsStringToJuce (file).convertToPrecomposedUnicode(); if (wildcardUTF8 == nullptr) wildcardUTF8 = wildCard.toUTF8(); if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0) continue; const String fullPath (parentDir + filenameFound); updateStatInfoForFile (fullPath, isDir, fileSize, modTime, creationTime, isReadOnly); if (isHidden != nullptr) *isHidden = FileHelpers::isHiddenFile (fullPath); return true; } } } private: String parentDir, wildCard; NSDirectoryEnumerator* enumerator; JUCE_DECLARE_NON_COPYABLE (Pimpl) }; DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildcard) : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildcard)) { } DirectoryIterator::NativeIterator::~NativeIterator() { } bool DirectoryIterator::NativeIterator::next (String& filenameFound, bool* const isDir, bool* const isHidden, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } //============================================================================== bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { JUCE_AUTORELEASEPOOL { NSURL* filenameAsURL = [NSURL URLWithString: juceStringToNS (fileName)]; #if JUCE_IOS (void) parameters; return [[UIApplication sharedApplication] openURL: filenameAsURL]; #else NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; if (parameters.isEmpty()) return [workspace openFile: juceStringToNS (fileName)] || [workspace openURL: filenameAsURL]; const File file (fileName); if (file.isBundle()) { StringArray params; params.addTokens (parameters, true); NSMutableArray* paramArray = [[[NSMutableArray alloc] init] autorelease]; for (int i = 0; i < params.size(); ++i) [paramArray addObject: juceStringToNS (params[i])]; NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease]; [dict setObject: paramArray forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")]; return [workspace launchApplicationAtURL: filenameAsURL options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance configuration: dict error: nil]; } if (file.exists()) return FileHelpers::launchExecutable ("\"" + fileName + "\" " + parameters); return false; #endif } } void File::revealToUser() const { #if ! JUCE_IOS if (exists()) [[NSWorkspace sharedWorkspace] selectFile: juceStringToNS (getFullPathName()) inFileViewerRootedAtPath: nsEmptyString()]; else if (getParentDirectory().exists()) getParentDirectory().revealToUser(); #endif } //============================================================================== OSType File::getMacOSType() const { JUCE_AUTORELEASEPOOL { #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) NSDictionary* fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath: juceStringToNS (getFullPathName()) error: nil]; #else // (the cast here avoids a deprecation warning) NSDictionary* fileDict = [((id) [NSFileManager defaultManager]) fileAttributesAtPath: juceStringToNS (getFullPathName()) traverseLink: NO]; #endif return [fileDict fileHFSTypeCode]; } } bool File::isBundle() const { #if JUCE_IOS return false; // xxx can't find a sensible way to do this without trying to open the bundle.. #else JUCE_AUTORELEASEPOOL { return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (getFullPathName())]; } #endif } #if JUCE_MAC void File::addToDock() const { // check that it's not already there... if (! juce_getOutputFromCommand ("defaults read com.apple.dock persistent-apps").containsIgnoreCase (getFullPathName())) { juce_runSystemCommand ("defaults write com.apple.dock persistent-apps -array-add \"tile-datafile-data_CFURLString" + getFullPathName() + "_CFURLStringType0\""); juce_runSystemCommand ("osascript -e \"tell application \\\"Dock\\\" to quit\""); } } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm000066400000000000000000000403001320201440200306370ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ void MACAddress::findAllAddresses (Array& result) { ifaddrs* addrs = nullptr; if (getifaddrs (&addrs) == 0) { for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next) { sockaddr_storage* sto = (sockaddr_storage*) cursor->ifa_addr; if (sto->ss_family == AF_LINK) { const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; #ifndef IFT_ETHER enum { IFT_ETHER = 6 }; #endif if (sadd->sdl_type == IFT_ETHER) { MACAddress ma (MACAddress (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen)); if (! ma.isNull()) result.addIfNotAlreadyThere (ma); } } } freeifaddrs (addrs); } } //============================================================================== bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, const String& emailSubject, const String& bodyText, const StringArray& filesToAttach) { #if JUCE_IOS (void) targetEmailAddress; (void) emailSubject; (void) bodyText; (void) filesToAttach; //xxx probably need to use MFMailComposeViewController jassertfalse; return false; #else JUCE_AUTORELEASEPOOL { String script; script << "tell application \"Mail\"\r\n" "set newMessage to make new outgoing message with properties {subject:\"" << emailSubject.replace ("\"", "\\\"") << "\", content:\"" << bodyText.replace ("\"", "\\\"") << "\" & return & return}\r\n" "tell newMessage\r\n" "set visible to true\r\n" "set sender to \"sdfsdfsdfewf\"\r\n" "make new to recipient at end of to recipients with properties {address:\"" << targetEmailAddress << "\"}\r\n"; for (int i = 0; i < filesToAttach.size(); ++i) { script << "tell content\r\n" "make new attachment with properties {file name:\"" << filesToAttach[i].replace ("\"", "\\\"") << "\"} at after the last paragraph\r\n" "end tell\r\n"; } script << "end tell\r\n" "end tell\r\n"; NSAppleScript* s = [[NSAppleScript alloc] initWithSource: juceStringToNS (script)]; NSDictionary* error = nil; const bool ok = [s executeAndReturnError: &error] != nil; [s release]; return ok; } #endif } //============================================================================== class URLConnectionState : public Thread { public: URLConnectionState (NSURLRequest* req, const int maxRedirects) : Thread ("http connection"), contentLength (-1), delegate (nil), request ([req retain]), connection (nil), data ([[NSMutableData data] retain]), headers (nil), statusCode (0), initialised (false), hasFailed (false), hasFinished (false), numRedirectsToFollow (maxRedirects), numRedirects (0), latestTotalBytes (0) { static DelegateClass cls; delegate = [cls.createInstance() init]; DelegateClass::setState (delegate, this); } ~URLConnectionState() { stop(); [connection release]; [data release]; [request release]; [headers release]; [delegate release]; } bool start (URL::OpenStreamProgressCallback* callback, void* context) { startThread(); while (isThreadRunning() && ! initialised) { if (callback != nullptr) callback (context, latestTotalBytes, (int) [[request HTTPBody] length]); Thread::sleep (1); } return connection != nil && ! hasFailed; } void stop() { [connection cancel]; stopThread (10000); } int read (char* dest, int numBytes) { int numDone = 0; while (numBytes > 0) { const int available = jmin (numBytes, (int) [data length]); if (available > 0) { const ScopedLock sl (dataLock); [data getBytes: dest length: (NSUInteger) available]; [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; numDone += available; numBytes -= available; dest += available; } else { if (hasFailed || hasFinished) break; Thread::sleep (1); } } return numDone; } void didReceiveResponse (NSURLResponse* response) { { const ScopedLock sl (dataLock); [data setLength: 0]; } contentLength = [response expectedContentLength]; [headers release]; headers = nil; if ([response isKindOfClass: [NSHTTPURLResponse class]]) { NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; headers = [[httpResponse allHeaderFields] retain]; statusCode = (int) [httpResponse statusCode]; } initialised = true; } NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) { if (redirectResponse != nullptr) { if (numRedirects >= numRedirectsToFollow) return nil; // Cancel redirect and allow connection to continue ++numRedirects; } return newRequest; } void didFailWithError (NSError* error) { DBG (nsStringToJuce ([error description])); (void) error; hasFailed = true; initialised = true; signalThreadShouldExit(); } void didReceiveData (NSData* newData) { const ScopedLock sl (dataLock); [data appendData: newData]; initialised = true; } void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) { latestTotalBytes = static_cast (totalBytesWritten); } void finishedLoading() { hasFinished = true; initialised = true; signalThreadShouldExit(); } void run() override { connection = [[NSURLConnection alloc] initWithRequest: request delegate: delegate]; while (! threadShouldExit()) { JUCE_AUTORELEASEPOOL { [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; } } } int64 contentLength; CriticalSection dataLock; NSObject* delegate; NSURLRequest* request; NSURLConnection* connection; NSMutableData* data; NSDictionary* headers; int statusCode; bool initialised, hasFailed, hasFinished; const int numRedirectsToFollow; int numRedirects; int latestTotalBytes; private: //============================================================================== struct DelegateClass : public ObjCClass { DelegateClass() : ObjCClass ("JUCEAppDelegate_") { addIvar ("state"); addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), connectionDidSendBodyData, "v@:@iii"); addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@@"); registerClass(); } static void setState (id self, URLConnectionState* state) { object_setInstanceVariable (self, "state", state); } static URLConnectionState* getState (id self) { return getIvar (self, "state"); } private: static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) { getState (self)->didReceiveResponse (response); } static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) { getState (self)->didFailWithError (error); } static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) { getState (self)->didReceiveData (newData); } static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) { return getState (self)->willSendRequest (request, response); } static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) { getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); } static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) { getState (self)->finishedLoading(); } }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionState) }; //============================================================================== class WebInputStream : public InputStream { public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, const int numRedirectsToFollow_) : statusCode (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (numRedirectsToFollow_) { JUCE_AUTORELEASEPOOL { createConnection (progressCallback, progressCallbackContext); if (connection != nullptr && connection->headers != nil) { statusCode = connection->statusCode; if (responseHeaders != nullptr) { NSEnumerator* enumerator = [connection->headers keyEnumerator]; while (NSString* key = [enumerator nextObject]) responseHeaders->set (nsStringToJuce (key), nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); } } } } //============================================================================== bool isError() const { return connection == nullptr; } int64 getTotalLength() override { return connection == nullptr ? -1 : connection->contentLength; } bool isExhausted() override { return finished; } int64 getPosition() override { return position; } int read (void* buffer, int bytesToRead) override { jassert (buffer != nullptr && bytesToRead >= 0); if (finished || isError()) return 0; JUCE_AUTORELEASEPOOL { const int bytesRead = connection->read (static_cast (buffer), bytesToRead); position += bytesRead; if (bytesRead == 0) finished = true; return bytesRead; } } bool setPosition (int64 wantedPos) override { if (wantedPos != position) { finished = false; if (wantedPos < position) { connection = nullptr; position = 0; createConnection (0, 0); } skipNextBytes (wantedPos - position); } return true; } int statusCode; private: ScopedPointer connection; String address, headers; MemoryBlock postData; int64 position; bool finished; const bool isPost; const int timeOutMs; const int numRedirectsToFollow; void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { jassert (connection == nullptr); NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (address)] cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; if (req != nil) { [req setHTTPMethod: nsStringLiteral (isPost ? "POST" : "GET")]; //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; StringArray headerLines; headerLines.addLines (headers); headerLines.removeEmptyStrings (true); for (int i = 0; i < headerLines.size(); ++i) { const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim()); const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim()); if (key.isNotEmpty() && value.isNotEmpty()) [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; } if (isPost && postData.getSize() > 0) [req setHTTPBody: [NSData dataWithBytes: postData.getData() length: postData.getSize()]]; connection = new URLConnectionState (req, numRedirectsToFollow); if (! connection->start (progressCallback, progressCallbackContext)) connection = nullptr; } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm000066400000000000000000000100121320201440200306340ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ String String::fromCFString (CFStringRef cfString) { if (cfString == 0) return String(); CFRange range = { 0, CFStringGetLength (cfString) }; CFIndex bytesNeeded = 0; CFStringGetBytes (cfString, range, kCFStringEncodingUTF8, 0, false, nullptr, 0, &bytesNeeded); HeapBlock utf8 ((size_t) bytesNeeded + 1); CFStringGetBytes (cfString, range, kCFStringEncodingUTF8, 0, false, utf8, bytesNeeded + 1, nullptr); return String (CharPointer_UTF8 ((const CharPointer_UTF8::CharType*) utf8.getData()), CharPointer_UTF8 ((const CharPointer_UTF8::CharType*) utf8.getData() + bytesNeeded)); } CFStringRef String::toCFString() const { const char* const utf8 = toRawUTF8(); return CFStringCreateWithBytes (kCFAllocatorDefault, (const UInt8*) utf8, (CFIndex) strlen (utf8), kCFStringEncodingUTF8, false); } String String::convertToPrecomposedUnicode() const { #if JUCE_IOS JUCE_AUTORELEASEPOOL { return nsStringToJuce ([juceStringToNS (*this) precomposedStringWithCanonicalMapping]); } #else UnicodeMapping map; map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, kUnicodeNoSubset, kTextEncodingDefaultFormat); map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, kUnicodeCanonicalCompVariant, kTextEncodingDefaultFormat); map.mappingVersion = kUnicodeUseLatestMapping; UnicodeToTextInfo conversionInfo = 0; String result; if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr) { const size_t bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (getCharPointer()); HeapBlock tempOut; tempOut.calloc (bytesNeeded + 4); ByteCount bytesRead = 0; ByteCount outputBufferSize = 0; if (ConvertFromUnicodeToText (conversionInfo, bytesNeeded, (ConstUniCharArrayPtr) toUTF16().getAddress(), kUnicodeDefaultDirectionMask, 0, 0, 0, 0, bytesNeeded, &bytesRead, &outputBufferSize, tempOut) == noErr) { result = String (CharPointer_UTF16 ((CharPointer_UTF16::CharType*) tempOut.getData())); } DisposeUnicodeToTextInfo (&conversionInfo); } return result; #endif } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm000066400000000000000000000224011320201440200315130ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ ScopedAutoReleasePool::ScopedAutoReleasePool() { pool = [[NSAutoreleasePool alloc] init]; } ScopedAutoReleasePool::~ScopedAutoReleasePool() { [((NSAutoreleasePool*) pool) release]; } //============================================================================== void Logger::outputDebugString (const String& text) { // Would prefer to use std::cerr here, but avoiding it for // the moment, due to clang JIT linkage problems. fputs (text.toRawUTF8(), stderr); fputs ("\n", stderr); fflush (stderr); } //============================================================================== namespace SystemStatsHelpers { #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM static void doCPUID (uint32& a, uint32& b, uint32& c, uint32& d, uint32 type) { uint32 la = a, lb = b, lc = c, ld = d; asm ("mov %%ebx, %%esi \n\t" "cpuid \n\t" "xchg %%esi, %%ebx" : "=a" (la), "=S" (lb), "=c" (lc), "=d" (ld) : "a" (type) #if JUCE_64BIT , "b" (lb), "c" (lc), "d" (ld) #endif ); a = la; b = lb; c = lc; d = ld; } #endif } //============================================================================== void CPUInformation::initialise() noexcept { #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM uint32 a = 0, b = 0, d = 0, c = 0; SystemStatsHelpers::doCPUID (a, b, c, d, 1); hasMMX = (d & (1u << 23)) != 0; hasSSE = (d & (1u << 25)) != 0; hasSSE2 = (d & (1u << 26)) != 0; has3DNow = (b & (1u << 31)) != 0; hasSSE3 = (c & (1u << 0)) != 0; hasSSSE3 = (c & (1u << 9)) != 0; hasAVX = (c & (1u << 28)) != 0; #endif #if JUCE_IOS || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) numCpus = (int) [[NSProcessInfo processInfo] activeProcessorCount]; #else numCpus = (int) MPProcessors(); #endif } #if JUCE_MAC struct RLimitInitialiser { RLimitInitialiser() { rlimit lim; getrlimit (RLIMIT_NOFILE, &lim); lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; setrlimit (RLIMIT_NOFILE, &lim); } }; static RLimitInitialiser rLimitInitialiser; #endif //============================================================================== #if ! JUCE_IOS static String getOSXVersion() { JUCE_AUTORELEASEPOOL { NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile: nsStringLiteral ("/System/Library/CoreServices/SystemVersion.plist")]; return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); } } #endif SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { #if JUCE_IOS return iOS; #else StringArray parts; parts.addTokens (getOSXVersion(), ".", StringRef()); jassert (parts[0].getIntValue() == 10); const int major = parts[1].getIntValue(); jassert (major > 2); return (OperatingSystemType) (major + MacOSX_10_4 - 4); #endif } String SystemStats::getOperatingSystemName() { #if JUCE_IOS return "iOS " + nsStringToJuce ([[UIDevice currentDevice] systemVersion]); #else return "Mac OSX " + getOSXVersion(); #endif } String SystemStats::getDeviceDescription() { #if JUCE_IOS return nsStringToJuce ([[UIDevice currentDevice] model]); #else return String(); #endif } bool SystemStats::isOperatingSystem64Bit() { #if JUCE_IOS return false; #elif JUCE_64BIT return true; #else return getOperatingSystemType() >= MacOSX_10_6; #endif } int SystemStats::getMemorySizeInMegabytes() { uint64 mem = 0; size_t memSize = sizeof (mem); int mib[] = { CTL_HW, HW_MEMSIZE }; sysctl (mib, 2, &mem, &memSize, 0, 0); return (int) (mem / (1024 * 1024)); } String SystemStats::getCpuVendor() { #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM uint32 dummy = 0; uint32 vendor[4] = { 0 }; SystemStatsHelpers::doCPUID (dummy, vendor[0], vendor[2], vendor[1], 0); return String (reinterpret_cast (vendor), 12); #else return String(); #endif } int SystemStats::getCpuSpeedInMegaherz() { uint64 speedHz = 0; size_t speedSize = sizeof (speedHz); int mib[] = { CTL_HW, HW_CPU_FREQ }; sysctl (mib, 2, &speedHz, &speedSize, 0, 0); #if JUCE_BIG_ENDIAN if (speedSize == 4) speedHz >>= 32; #endif return (int) (speedHz / 1000000); } //============================================================================== String SystemStats::getLogonName() { return nsStringToJuce (NSUserName()); } String SystemStats::getFullUserName() { return nsStringToJuce (NSFullUserName()); } String SystemStats::getComputerName() { char name [256] = { 0 }; if (gethostname (name, sizeof (name) - 1) == 0) return String (name).upToLastOccurrenceOf (".local", false, true); return String(); } static String getLocaleValue (CFStringRef key) { CFLocaleRef cfLocale = CFLocaleCopyCurrent(); const String result (String::fromCFString ((CFStringRef) CFLocaleGetValue (cfLocale, key))); CFRelease (cfLocale); return result; } String SystemStats::getUserLanguage() { return getLocaleValue (kCFLocaleLanguageCode); } String SystemStats::getUserRegion() { return getLocaleValue (kCFLocaleCountryCode); } String SystemStats::getDisplayLanguage() { CFArrayRef cfPrefLangs = CFLocaleCopyPreferredLanguages(); const String result (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (cfPrefLangs, 0))); CFRelease (cfPrefLangs); return result; } //============================================================================== /* NB: these are kept outside the HiResCounterInfo struct and initialised to 1 to avoid division-by-zero errors if some other static constructor calls us before this file's static constructors have had a chance to fill them in correctly.. */ static uint64 hiResCounterNumerator = 0, hiResCounterDenominator = 1; class HiResCounterInfo { public: HiResCounterInfo() { mach_timebase_info_data_t timebase; (void) mach_timebase_info (&timebase); if (timebase.numer % 1000000 == 0) { hiResCounterNumerator = timebase.numer / 1000000; hiResCounterDenominator = timebase.denom; } else { hiResCounterNumerator = timebase.numer; hiResCounterDenominator = timebase.denom * (uint64) 1000000; } highResTimerFrequency = (timebase.denom * (uint64) 1000000000) / timebase.numer; highResTimerToMillisecRatio = hiResCounterNumerator / (double) hiResCounterDenominator; } uint32 millisecondsSinceStartup() const noexcept { return (uint32) ((mach_absolute_time() * hiResCounterNumerator) / hiResCounterDenominator); } double getMillisecondCounterHiRes() const noexcept { return mach_absolute_time() * highResTimerToMillisecRatio; } int64 highResTimerFrequency; private: double highResTimerToMillisecRatio; }; static HiResCounterInfo hiResCounterInfo; uint32 juce_millisecondsSinceStartup() noexcept { return hiResCounterInfo.millisecondsSinceStartup(); } double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterInfo.getMillisecondCounterHiRes(); } int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterInfo.highResTimerFrequency; } int64 Time::getHighResolutionTicks() noexcept { return (int64) mach_absolute_time(); } bool Time::setSystemTimeToThisTime() const { jassertfalse; return false; } //============================================================================== int SystemStats::getPageSize() { return (int) NSPageSize(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm000066400000000000000000000056121320201440200306070ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ /* Note that a lot of methods that you'd expect to find in this file actually live in juce_posix_SharedCode.h! */ #if JUCE_IOS bool isIOSAppActive = true; #endif //============================================================================== JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { #if JUCE_MAC return [NSApp isActive]; #else return isIOSAppActive; #endif } JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() { #if JUCE_MAC [NSApp activateIgnoringOtherApps: YES]; #endif } JUCE_API void JUCE_CALLTYPE Process::hide() { #if JUCE_MAC [NSApp hide: nil]; #endif } JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { jassertfalse; } JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() { jassertfalse; } JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) { // xxx } //============================================================================== JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() { static char testResult = 0; if (testResult == 0) { struct kinfo_proc info; int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; size_t sz = sizeof (info); sysctl (m, 4, &info, &sz, 0, 0); testResult = ((info.kp_proc.p_flag & P_TRACED) != 0) ? 1 : -1; } return testResult > 0; } JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() { return juce_isRunningUnderDebugger(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h000066400000000000000000000140541320201440200312440ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_OSX_OBJCHELPERS_H_INCLUDED #define JUCE_OSX_OBJCHELPERS_H_INCLUDED /* This file contains a few helper functions that are used internally but which need to be kept away from the public headers because they use obj-C symbols. */ namespace { //============================================================================== static inline String nsStringToJuce (NSString* s) { return CharPointer_UTF8 ([s UTF8String]); } static inline NSString* juceStringToNS (const String& s) { return [NSString stringWithUTF8String: s.toUTF8()]; } static inline NSString* nsStringLiteral (const char* const s) noexcept { return [NSString stringWithUTF8String: s]; } static inline NSString* nsEmptyString() noexcept { return [NSString string]; } #if JUCE_MAC template static NSRect makeNSRect (const RectangleType& r) noexcept { return NSMakeRect (static_cast (r.getX()), static_cast (r.getY()), static_cast (r.getWidth()), static_cast (r.getHeight())); } // These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions.. typedef id (*MsgSendSuperFn) (struct objc_super*, SEL, ...); static inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; } #if ! JUCE_PPC typedef double (*MsgSendFPRetFn) (id, SEL op, ...); static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; } #endif #endif } //============================================================================== template struct NSObjectRetainer { inline NSObjectRetainer (ObjectType* o) : object (o) { [object retain]; } inline ~NSObjectRetainer() { [object release]; } ObjectType* object; }; //============================================================================== template struct ObjCClass { ObjCClass (const char* nameRoot) : cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0)) { } ~ObjCClass() { objc_disposeClassPair (cls); } void registerClass() { objc_registerClassPair (cls); } SuperclassType* createInstance() const { return class_createInstance (cls, 0); } template void addIvar (const char* name) { BOOL b = class_addIvar (cls, name, sizeof (Type), (uint8_t) rint (log2 (sizeof (Type))), @encode (Type)); jassert (b); (void) b; } template void addMethod (SEL selector, FunctionType callbackFn, const char* signature) { BOOL b = class_addMethod (cls, selector, (IMP) callbackFn, signature); jassert (b); (void) b; } template void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2) { addMethod (selector, callbackFn, (String (sig1) + sig2).toUTF8()); } template void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3) { addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3).toUTF8()); } template void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3, const char* sig4) { addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3 + sig4).toUTF8()); } void addProtocol (Protocol* protocol) { BOOL b = class_addProtocol (cls, protocol); jassert (b); (void) b; } #if JUCE_MAC static id sendSuperclassMessage (id self, SEL selector) { objc_super s = { self, [SuperclassType class] }; return getMsgSendSuperFn() (&s, selector); } #endif template static Type getIvar (id self, const char* name) { void* v = nullptr; object_getInstanceVariable (self, name, &v); return static_cast (v); } Class cls; private: static String getRandomisedName (const char* root) { return root + String::toHexString (juce::Random::getSystemRandom().nextInt64()); } JUCE_DECLARE_NON_COPYABLE (ObjCClass) }; #endif // JUCE_OSX_OBJCHELPERS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp000066400000000000000000000157071320201440200316400ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ class NamedPipe::Pimpl { public: Pimpl (const String& pipePath, bool createPipe) : pipeInName (pipePath + "_in"), pipeOutName (pipePath + "_out"), pipeIn (-1), pipeOut (-1), createdPipe (createPipe), stopReadOperation (false) { signal (SIGPIPE, signalHandler); juce_siginterrupt (SIGPIPE, 1); } ~Pimpl() { if (pipeIn != -1) ::close (pipeIn); if (pipeOut != -1) ::close (pipeOut); if (createdPipe) { unlink (pipeInName.toUTF8()); unlink (pipeOutName.toUTF8()); } } int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); if (pipeIn == -1) { pipeIn = openPipe (createdPipe ? pipeInName : pipeOutName, O_RDWR | O_NONBLOCK, timeoutEnd); if (pipeIn == -1) return -1; } int bytesRead = 0; while (bytesRead < maxBytesToRead) { const int bytesThisTime = maxBytesToRead - bytesRead; const int numRead = (int) ::read (pipeIn, destBuffer, (size_t) bytesThisTime); if (numRead <= 0) { if (errno != EWOULDBLOCK || stopReadOperation || hasExpired (timeoutEnd)) return -1; const int maxWaitingTime = 30; waitForInput (pipeIn, timeoutEnd == 0 ? maxWaitingTime : jmin (maxWaitingTime, (int) (timeoutEnd - Time::getMillisecondCounter()))); continue; } bytesRead += numRead; destBuffer += numRead; } return bytesRead; } int write (const char* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); if (pipeOut == -1) { pipeOut = openPipe (createdPipe ? pipeOutName : pipeInName, O_WRONLY, timeoutEnd); if (pipeOut == -1) return -1; } int bytesWritten = 0; while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd)) { const int bytesThisTime = numBytesToWrite - bytesWritten; const int numWritten = (int) ::write (pipeOut, sourceBuffer, (size_t) bytesThisTime); if (numWritten <= 0) return -1; bytesWritten += numWritten; sourceBuffer += numWritten; } return bytesWritten; } bool createFifos() const { return (mkfifo (pipeInName .toUTF8(), 0666) == 0 || errno == EEXIST) && (mkfifo (pipeOutName.toUTF8(), 0666) == 0 || errno == EEXIST); } const String pipeInName, pipeOutName; int pipeIn, pipeOut; const bool createdPipe; bool stopReadOperation; private: static void signalHandler (int) {} static uint32 getTimeoutEnd (const int timeOutMilliseconds) { return timeOutMilliseconds >= 0 ? Time::getMillisecondCounter() + (uint32) timeOutMilliseconds : 0; } static bool hasExpired (const uint32 timeoutEnd) { return timeoutEnd != 0 && Time::getMillisecondCounter() >= timeoutEnd; } int openPipe (const String& name, int flags, const uint32 timeoutEnd) { for (;;) { const int p = ::open (name.toUTF8(), flags); if (p != -1 || hasExpired (timeoutEnd) || stopReadOperation) return p; Thread::sleep (2); } } static void waitForInput (const int handle, const int timeoutMsecs) noexcept { struct timeval timeout; timeout.tv_sec = timeoutMsecs / 1000; timeout.tv_usec = (timeoutMsecs % 1000) * 1000; fd_set rset; FD_ZERO (&rset); FD_SET (handle, &rset); select (handle + 1, &rset, nullptr, 0, &timeout); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; void NamedPipe::close() { if (pimpl != nullptr) { pimpl->stopReadOperation = true; char buffer[1] = { 0 }; ssize_t done = ::write (pimpl->pipeIn, buffer, 1); (void) done; ScopedWriteLock sl (lock); pimpl = nullptr; } } bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) { #if JUCE_IOS pimpl = new Pimpl (File::getSpecialLocation (File::tempDirectory) .getChildFile (File::createLegalFileName (pipeName)).getFullPathName(), createPipe); #else String file (pipeName); if (! File::isAbsolutePath (file)) file = "/tmp/" + File::createLegalFileName (file); pimpl = new Pimpl (file, createPipe); #endif if (createPipe && ! pimpl->createFifos()) { pimpl = nullptr; return false; } return true; } int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { ScopedReadLock sl (lock); return pimpl != nullptr ? pimpl->read (static_cast (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1; } int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { ScopedReadLock sl (lock); return pimpl != nullptr ? pimpl->write (static_cast (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h000066400000000000000000001077001320201440200314370ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ CriticalSection::CriticalSection() noexcept { pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); #endif pthread_mutex_init (&lock, &atts); pthread_mutexattr_destroy (&atts); } CriticalSection::~CriticalSection() noexcept { pthread_mutex_destroy (&lock); } void CriticalSection::enter() const noexcept { pthread_mutex_lock (&lock); } bool CriticalSection::tryEnter() const noexcept { return pthread_mutex_trylock (&lock) == 0; } void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&lock); } //============================================================================== WaitableEvent::WaitableEvent (const bool useManualReset) noexcept : triggered (false), manualReset (useManualReset) { pthread_cond_init (&condition, 0); pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); #endif pthread_mutex_init (&mutex, &atts); } WaitableEvent::~WaitableEvent() noexcept { pthread_cond_destroy (&condition); pthread_mutex_destroy (&mutex); } bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept { pthread_mutex_lock (&mutex); if (! triggered) { if (timeOutMillisecs < 0) { do { pthread_cond_wait (&condition, &mutex); } while (! triggered); } else { struct timeval now; gettimeofday (&now, 0); struct timespec time; time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000); time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; if (time.tv_nsec >= 1000000000) { time.tv_nsec -= 1000000000; time.tv_sec++; } do { if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT) { pthread_mutex_unlock (&mutex); return false; } } while (! triggered); } } if (! manualReset) triggered = false; pthread_mutex_unlock (&mutex); return true; } void WaitableEvent::signal() const noexcept { pthread_mutex_lock (&mutex); if (! triggered) { triggered = true; pthread_cond_broadcast (&condition); } pthread_mutex_unlock (&mutex); } void WaitableEvent::reset() const noexcept { pthread_mutex_lock (&mutex); triggered = false; pthread_mutex_unlock (&mutex); } //============================================================================== void JUCE_CALLTYPE Thread::sleep (int millisecs) { struct timespec time; time.tv_sec = millisecs / 1000; time.tv_nsec = (millisecs % 1000) * 1000000; nanosleep (&time, nullptr); } void JUCE_CALLTYPE Process::terminate() { #if JUCE_ANDROID _exit (EXIT_FAILURE); #else std::_Exit (EXIT_FAILURE); #endif } //============================================================================== const juce_wchar File::separator = '/'; const String File::separatorString ("/"); //============================================================================== File File::getCurrentWorkingDirectory() { HeapBlock heapBuffer; char localBuffer [1024]; char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1); size_t bufferSize = 4096; while (cwd == nullptr && errno == ERANGE) { heapBuffer.malloc (bufferSize); cwd = getcwd (heapBuffer, bufferSize - 1); bufferSize += 1024; } return File (CharPointer_UTF8 (cwd)); } bool File::setAsCurrentWorkingDirectory() const { return chdir (getFullPathName().toUTF8()) == 0; } //============================================================================== // The unix siginterrupt function is deprecated - this does the same job. int juce_siginterrupt (int sig, int flag) { struct ::sigaction act; (void) ::sigaction (sig, nullptr, &act); if (flag != 0) act.sa_flags &= ~SA_RESTART; else act.sa_flags |= SA_RESTART; return ::sigaction (sig, &act, nullptr); } //============================================================================== namespace { #if JUCE_LINUX || (JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T) // (this iOS stuff is to avoid a simulator bug) typedef struct stat64 juce_statStruct; #define JUCE_STAT stat64 #else typedef struct stat juce_statStruct; #define JUCE_STAT stat #endif bool juce_stat (const String& fileName, juce_statStruct& info) { return fileName.isNotEmpty() && JUCE_STAT (fileName.toUTF8(), &info) == 0; } // if this file doesn't exist, find a parent of it that does.. bool juce_doStatFS (File f, struct statfs& result) { for (int i = 5; --i >= 0;) { if (f.exists()) break; f = f.getParentDirectory(); } return statfs (f.getFullPathName().toUTF8(), &result) == 0; } #if (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || JUCE_IOS static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_birthtime; } #else static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_ctime; } #endif void updateStatInfoForFile (const String& path, bool* const isDir, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { if (isDir != nullptr || fileSize != nullptr || modTime != nullptr || creationTime != nullptr) { juce_statStruct info; const bool statOk = juce_stat (path, info); if (isDir != nullptr) *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); if (fileSize != nullptr) *fileSize = statOk ? (int64) info.st_size : 0; if (modTime != nullptr) *modTime = Time (statOk ? (int64) info.st_mtime * 1000 : 0); if (creationTime != nullptr) *creationTime = Time (statOk ? getCreationTime (info) * 1000 : 0); } if (isReadOnly != nullptr) *isReadOnly = access (path.toUTF8(), W_OK) != 0; } Result getResultForErrno() { return Result::fail (String (strerror (errno))); } Result getResultForReturnValue (int value) { return value == -1 ? getResultForErrno() : Result::ok(); } int getFD (void* handle) noexcept { return (int) (pointer_sized_int) handle; } void* fdToVoidPointer (int fd) noexcept { return (void*) (pointer_sized_int) fd; } } bool File::isDirectory() const { juce_statStruct info; return fullPath.isNotEmpty() && (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0)); } bool File::exists() const { return fullPath.isNotEmpty() && access (fullPath.toUTF8(), F_OK) == 0; } bool File::existsAsFile() const { return exists() && ! isDirectory(); } int64 File::getSize() const { juce_statStruct info; return juce_stat (fullPath, info) ? info.st_size : 0; } uint64 File::getFileIdentifier() const { juce_statStruct info; return juce_stat (fullPath, info) ? (uint64) info.st_ino : 0; } //============================================================================== bool File::hasWriteAccess() const { if (exists()) return access (fullPath.toUTF8(), W_OK) == 0; if ((! isDirectory()) && fullPath.containsChar (separator)) return getParentDirectory().hasWriteAccess(); return false; } static bool setFileModeFlags (const String& fullPath, mode_t flags, bool shouldSet) noexcept { juce_statStruct info; if (! juce_stat (fullPath, info)) return false; info.st_mode &= 0777; if (shouldSet) info.st_mode |= flags; else info.st_mode &= ~flags; return chmod (fullPath.toUTF8(), info.st_mode) == 0; } bool File::setFileReadOnlyInternal (bool shouldBeReadOnly) const { // Hmm.. should we give global write permission or just the current user? return setFileModeFlags (fullPath, S_IWUSR | S_IWGRP | S_IWOTH, ! shouldBeReadOnly); } bool File::setFileExecutableInternal (bool shouldBeExecutable) const { return setFileModeFlags (fullPath, S_IXUSR | S_IXGRP | S_IXOTH, shouldBeExecutable); } void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const { modificationTime = 0; accessTime = 0; creationTime = 0; juce_statStruct info; if (juce_stat (fullPath, info)) { modificationTime = (int64) info.st_mtime * 1000; accessTime = (int64) info.st_atime * 1000; creationTime = (int64) info.st_ctime * 1000; } } bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 /*creationTime*/) const { juce_statStruct info; if ((modificationTime != 0 || accessTime != 0) && juce_stat (fullPath, info)) { struct utimbuf times; times.actime = accessTime != 0 ? (time_t) (accessTime / 1000) : info.st_atime; times.modtime = modificationTime != 0 ? (time_t) (modificationTime / 1000) : info.st_mtime; return utime (fullPath.toUTF8(), ×) == 0; } return false; } bool File::deleteFile() const { if (! exists()) return true; if (isDirectory()) return rmdir (fullPath.toUTF8()) == 0; return remove (fullPath.toUTF8()) == 0; } bool File::moveInternal (const File& dest) const { if (rename (fullPath.toUTF8(), dest.getFullPathName().toUTF8()) == 0) return true; if (hasWriteAccess() && copyInternal (dest)) { if (deleteFile()) return true; dest.deleteFile(); } return false; } Result File::createDirectoryInternal (const String& fileName) const { return getResultForReturnValue (mkdir (fileName.toUTF8(), 0777)); } //===================================================================== int64 juce_fileSetPosition (void* handle, int64 pos) { if (handle != 0 && lseek (getFD (handle), pos, SEEK_SET) == pos) return pos; return -1; } void FileInputStream::openHandle() { const int f = open (file.getFullPathName().toUTF8(), O_RDONLY, 00644); if (f != -1) fileHandle = fdToVoidPointer (f); else status = getResultForErrno(); } FileInputStream::~FileInputStream() { if (fileHandle != 0) close (getFD (fileHandle)); } size_t FileInputStream::readInternal (void* const buffer, const size_t numBytes) { ssize_t result = 0; if (fileHandle != 0) { result = ::read (getFD (fileHandle), buffer, numBytes); if (result < 0) { status = getResultForErrno(); result = 0; } } return (size_t) result; } //============================================================================== void FileOutputStream::openHandle() { if (file.exists()) { const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644); if (f != -1) { currentPosition = lseek (f, 0, SEEK_END); if (currentPosition >= 0) { fileHandle = fdToVoidPointer (f); } else { status = getResultForErrno(); close (f); } } else { status = getResultForErrno(); } } else { const int f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644); if (f != -1) fileHandle = fdToVoidPointer (f); else status = getResultForErrno(); } } void FileOutputStream::closeHandle() { if (fileHandle != 0) { close (getFD (fileHandle)); fileHandle = 0; } } ssize_t FileOutputStream::writeInternal (const void* const data, const size_t numBytes) { ssize_t result = 0; if (fileHandle != 0) { result = ::write (getFD (fileHandle), data, numBytes); if (result == -1) status = getResultForErrno(); } return result; } void FileOutputStream::flushInternal() { if (fileHandle != 0) { if (fsync (getFD (fileHandle)) == -1) status = getResultForErrno(); #if JUCE_ANDROID // This stuff tells the OS to asynchronously update the metadata // that the OS has cached aboud the file - this metadata is used // when the device is acting as a USB drive, and unless it's explicitly // refreshed, it'll get out of step with the real file. const LocalRef t (javaString (file.getFullPathName())); android.activity.callVoidMethod (JuceAppActivity.scanFile, t.get()); #endif } } Result FileOutputStream::truncate() { if (fileHandle == 0) return status; flush(); return getResultForReturnValue (ftruncate (getFD (fileHandle), (off_t) currentPosition)); } //============================================================================== String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue) { if (const char* s = ::getenv (name.toUTF8())) return String::fromUTF8 (s); return defaultValue; } //============================================================================== void MemoryMappedFile::openInternal (const File& file, AccessMode mode) { jassert (mode == readOnly || mode == readWrite); if (range.getStart() > 0) { const long pageSize = sysconf (_SC_PAGE_SIZE); range.setStart (range.getStart() - (range.getStart() % pageSize)); } fileHandle = open (file.getFullPathName().toUTF8(), mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644); if (fileHandle != -1) { void* m = mmap (0, (size_t) range.getLength(), mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED, fileHandle, (off_t) range.getStart()); if (m != MAP_FAILED) { address = m; madvise (m, (size_t) range.getLength(), MADV_SEQUENTIAL); } else { range = Range(); } } } MemoryMappedFile::~MemoryMappedFile() { if (address != nullptr) munmap (address, (size_t) range.getLength()); if (fileHandle != 0) close (fileHandle); } //============================================================================== #if JUCE_PROJUCER_LIVE_BUILD extern "C" const char* juce_getCurrentExecutablePath(); #endif File juce_getExecutableFile(); File juce_getExecutableFile() { #if JUCE_PROJUCER_LIVE_BUILD return File (juce_getCurrentExecutablePath()); #elif JUCE_ANDROID return File (android.appFile); #else struct DLAddrReader { static String getFilename() { Dl_info exeInfo; dladdr ((void*) juce_getExecutableFile, &exeInfo); return CharPointer_UTF8 (exeInfo.dli_fname); } }; static String filename (DLAddrReader::getFilename()); return File::getCurrentWorkingDirectory().getChildFile (filename); #endif } //============================================================================== int64 File::getBytesFreeOnVolume() const { struct statfs buf; if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user return 0; } int64 File::getVolumeTotalSize() const { struct statfs buf; if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_blocks; return 0; } String File::getVolumeLabel() const { #if JUCE_MAC struct VolAttrBuf { u_int32_t length; attrreference_t mountPointRef; char mountPointSpace [MAXPATHLEN]; } attrBuf; struct attrlist attrList; zerostruct (attrList); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; File f (*this); for (;;) { if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0) return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset, (int) attrBuf.mountPointRef.attr_length); const File parent (f.getParentDirectory()); if (f == parent) break; f = parent; } #endif return String(); } int File::getVolumeSerialNumber() const { int result = 0; /* int fd = open (getFullPathName().toUTF8(), O_RDONLY | O_NONBLOCK); char info [512]; #ifndef HDIO_GET_IDENTITY #define HDIO_GET_IDENTITY 0x030d #endif if (ioctl (fd, HDIO_GET_IDENTITY, info) == 0) { DBG (String (info + 20, 20)); result = String (info + 20, 20).trim().getIntValue(); } close (fd);*/ return result; } //============================================================================== #if ! JUCE_IOS void juce_runSystemCommand (const String&); void juce_runSystemCommand (const String& command) { int result = system (command.toUTF8()); (void) result; } String juce_getOutputFromCommand (const String&); String juce_getOutputFromCommand (const String& command) { // slight bodge here, as we just pipe the output into a temp file and read it... const File tempFile (File::getSpecialLocation (File::tempDirectory) .getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false)); juce_runSystemCommand (command + " > " + tempFile.getFullPathName()); String result (tempFile.loadFileAsString()); tempFile.deleteFile(); return result; } #endif //============================================================================== #if JUCE_IOS class InterProcessLock::Pimpl { public: Pimpl (const String&, int) : handle (1), refCount (1) // On iOS just fake success.. { } int handle, refCount; }; #else class InterProcessLock::Pimpl { public: Pimpl (const String& lockName, const int timeOutMillisecs) : handle (0), refCount (1) { #if JUCE_MAC if (! createLockFile (File ("~/Library/Caches/com.juce.locks").getChildFile (lockName), timeOutMillisecs)) // Fallback if the user's home folder is on a network drive with no ability to lock.. createLockFile (File ("/tmp/com.juce.locks").getChildFile (lockName), timeOutMillisecs); #else File tempFolder ("/var/tmp"); if (! tempFolder.isDirectory()) tempFolder = "/tmp"; createLockFile (tempFolder.getChildFile (lockName), timeOutMillisecs); #endif } ~Pimpl() { closeFile(); } bool createLockFile (const File& file, const int timeOutMillisecs) { file.create(); handle = open (file.getFullPathName().toUTF8(), O_RDWR); if (handle != 0) { struct flock fl; zerostruct (fl); fl.l_whence = SEEK_SET; fl.l_type = F_WRLCK; const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; for (;;) { const int result = fcntl (handle, F_SETLK, &fl); if (result >= 0) return true; const int error = errno; if (error != EINTR) { if (error == EBADF || error == ENOTSUP) return false; if (timeOutMillisecs == 0 || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime)) break; Thread::sleep (10); } } } closeFile(); return true; // only false if there's a file system error. Failure to lock still returns true. } void closeFile() { if (handle != 0) { struct flock fl; zerostruct (fl); fl.l_whence = SEEK_SET; fl.l_type = F_UNLCK; while (! (fcntl (handle, F_SETLKW, &fl) >= 0 || errno != EINTR)) {} close (handle); handle = 0; } } int handle, refCount; }; #endif InterProcessLock::InterProcessLock (const String& nm) : name (nm) { } InterProcessLock::~InterProcessLock() { } bool InterProcessLock::enter (const int timeOutMillisecs) { const ScopedLock sl (lock); if (pimpl == nullptr) { pimpl = new Pimpl (name, timeOutMillisecs); if (pimpl->handle == 0) pimpl = nullptr; } else { pimpl->refCount++; } return pimpl != nullptr; } void InterProcessLock::exit() { const ScopedLock sl (lock); // Trying to release the lock too many times! jassert (pimpl != nullptr); if (pimpl != nullptr && --(pimpl->refCount) == 0) pimpl = nullptr; } //============================================================================== void JUCE_API juce_threadEntryPoint (void*); extern "C" void* threadEntryProc (void*); extern "C" void* threadEntryProc (void* userData) { JUCE_AUTORELEASEPOOL { #if JUCE_ANDROID const AndroidThreadScope androidEnv; #endif juce_threadEntryPoint (userData); } return nullptr; } void Thread::launchThread() { threadHandle = 0; pthread_t handle = 0; if (pthread_create (&handle, 0, threadEntryProc, this) == 0) { pthread_detach (handle); threadHandle = (void*) handle; threadId = (ThreadID) threadHandle; } } void Thread::closeThreadHandle() { threadId = 0; threadHandle = 0; } void Thread::killThread() { if (threadHandle != 0) { #if JUCE_ANDROID jassertfalse; // pthread_cancel not available! #else pthread_cancel ((pthread_t) threadHandle); #endif } } void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) { #if JUCE_IOS || (JUCE_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) JUCE_AUTORELEASEPOOL { [[NSThread currentThread] setName: juceStringToNS (name)]; } #elif JUCE_LINUX #if (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 pthread_setname_np (pthread_self(), name.toRawUTF8()); #else prctl (PR_SET_NAME, name.toRawUTF8(), 0, 0, 0); #endif #endif } bool Thread::setThreadPriority (void* handle, int priority) { struct sched_param param; int policy; priority = jlimit (0, 10, priority); if (handle == nullptr) handle = (void*) pthread_self(); if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0) return false; policy = priority == 0 ? SCHED_OTHER : SCHED_RR; const int minPriority = sched_get_priority_min (policy); const int maxPriority = sched_get_priority_max (policy); param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority; return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0; } Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId() { return (ThreadID) pthread_self(); } void JUCE_CALLTYPE Thread::yield() { sched_yield(); } //============================================================================== /* Remove this macro if you're having problems compiling the cpu affinity calls (the API for these has changed about quite a bit in various Linux versions, and a lot of distros seem to ship with obsolete versions) */ #if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES) #define SUPPORT_AFFINITIES 1 #endif void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) { #if SUPPORT_AFFINITIES cpu_set_t affinity; CPU_ZERO (&affinity); for (int i = 0; i < 32; ++i) if ((affinityMask & (1 << i)) != 0) CPU_SET (i, &affinity); #if (! JUCE_ANDROID) && ((! JUCE_LINUX) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004)) pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity); #else // NB: this call isn't really correct because it sets the affinity of the process, // not the thread. But it's included here as a fallback for people who are using // ridiculously old versions of glibc sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity); #endif sched_yield(); #else // affinities aren't supported because either the appropriate header files weren't found, // or the SUPPORT_AFFINITIES macro was turned off jassertfalse; ignoreUnused (affinityMask); #endif } //============================================================================== bool DynamicLibrary::open (const String& name) { close(); handle = dlopen (name.isEmpty() ? nullptr : name.toUTF8().getAddress(), RTLD_LOCAL | RTLD_NOW); return handle != nullptr; } void DynamicLibrary::close() { if (handle != nullptr) { dlclose (handle); handle = nullptr; } } void* DynamicLibrary::getFunction (const String& functionName) noexcept { return handle != nullptr ? dlsym (handle, functionName.toUTF8()) : nullptr; } //============================================================================== class ChildProcess::ActiveProcess { public: ActiveProcess (const StringArray& arguments, int streamFlags) : childPID (0), pipeHandle (0), readHandle (0) { // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX // you're trying to launch the .app folder rather than the actual binary inside it?) jassert ((! arguments[0].containsChar ('/')) || File::getCurrentWorkingDirectory().getChildFile (arguments[0]).existsAsFile()); int pipeHandles[2] = { 0 }; if (pipe (pipeHandles) == 0) { const pid_t result = fork(); if (result < 0) { close (pipeHandles[0]); close (pipeHandles[1]); } else if (result == 0) { // we're the child process.. close (pipeHandles[0]); // close the read handle if ((streamFlags & wantStdOut) != 0) dup2 (pipeHandles[1], 1); // turns the pipe into stdout else close (STDOUT_FILENO); if ((streamFlags & wantStdErr) != 0) dup2 (pipeHandles[1], 2); else close (STDERR_FILENO); close (pipeHandles[1]); Array argv; for (int i = 0; i < arguments.size(); ++i) if (arguments[i].isNotEmpty()) argv.add (const_cast (arguments[i].toUTF8().getAddress())); argv.add (nullptr); execvp (argv[0], argv.getRawDataPointer()); exit (-1); } else { // we're the parent process.. childPID = result; pipeHandle = pipeHandles[0]; close (pipeHandles[1]); // close the write handle } } } ~ActiveProcess() { if (readHandle != 0) fclose (readHandle); if (pipeHandle != 0) close (pipeHandle); } bool isRunning() const noexcept { if (childPID != 0) { int childState; const int pid = waitpid (childPID, &childState, WNOHANG); return pid == 0 || ! (WIFEXITED (childState) || WIFSIGNALED (childState)); } return false; } int read (void* const dest, const int numBytes) noexcept { jassert (dest != nullptr); #ifdef fdopen #error // the zlib headers define this function as NULL! #endif if (readHandle == 0 && childPID != 0) readHandle = fdopen (pipeHandle, "r"); if (readHandle != 0) return (int) fread (dest, 1, (size_t) numBytes, readHandle); return 0; } bool killProcess() const noexcept { return ::kill (childPID, SIGKILL) == 0; } uint32 getExitCode() const noexcept { if (childPID != 0) { int childState = 0; const int pid = waitpid (childPID, &childState, WNOHANG); if (pid >= 0 && WIFEXITED (childState)) return WEXITSTATUS (childState); } return 0; } int childPID; private: int pipeHandle; FILE* readHandle; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) }; bool ChildProcess::start (const String& command, int streamFlags) { return start (StringArray::fromTokens (command, true), streamFlags); } bool ChildProcess::start (const StringArray& args, int streamFlags) { if (args.size() == 0) return false; activeProcess = new ActiveProcess (args, streamFlags); if (activeProcess->childPID == 0) activeProcess = nullptr; return activeProcess != nullptr; } //============================================================================== struct HighResolutionTimer::Pimpl { Pimpl (HighResolutionTimer& t) : owner (t), thread (0), shouldStop (false) { } ~Pimpl() { jassert (thread == 0); } void start (int newPeriod) { if (periodMs != newPeriod) { if (thread != pthread_self()) { stop(); periodMs = newPeriod; shouldStop = false; if (pthread_create (&thread, nullptr, timerThread, this) == 0) setThreadToRealtime (thread, (uint64) newPeriod); else jassertfalse; } else { periodMs = newPeriod; shouldStop = false; } } } void stop() { if (thread != 0) { shouldStop = true; while (thread != 0 && thread != pthread_self()) Thread::yield(); } } HighResolutionTimer& owner; int volatile periodMs; private: pthread_t thread; bool volatile shouldStop; static void* timerThread (void* param) { #if JUCE_ANDROID const AndroidThreadScope androidEnv; #else int dummy; pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy); #endif reinterpret_cast (param)->timerThread(); return nullptr; } void timerThread() { int lastPeriod = periodMs; Clock clock (lastPeriod); while (! shouldStop) { clock.wait(); owner.hiResTimerCallback(); if (lastPeriod != periodMs) { lastPeriod = periodMs; clock = Clock (lastPeriod); } } periodMs = 0; thread = 0; } struct Clock { #if JUCE_MAC || JUCE_IOS Clock (double millis) noexcept { mach_timebase_info_data_t timebase; (void) mach_timebase_info (&timebase); delta = (((uint64_t) (millis * 1000000.0)) * timebase.denom) / timebase.numer; time = mach_absolute_time(); } void wait() noexcept { time += delta; mach_wait_until (time); } uint64_t time, delta; #elif JUCE_ANDROID Clock (double millis) noexcept : delta ((uint64) (millis * 1000000)) { } void wait() noexcept { struct timespec t; t.tv_sec = (time_t) (delta / 1000000000); t.tv_nsec = (long) (delta % 1000000000); nanosleep (&t, nullptr); } uint64 delta; #else Clock (double millis) noexcept : delta ((uint64) (millis * 1000000)) { struct timespec t; clock_gettime (CLOCK_MONOTONIC, &t); time = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec); } void wait() noexcept { time += delta; struct timespec t; t.tv_sec = (time_t) (time / 1000000000); t.tv_nsec = (long) (time % 1000000000); clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &t, nullptr); } uint64 time, delta; #endif }; static bool setThreadToRealtime (pthread_t thread, uint64 periodMs) { #if JUCE_MAC || JUCE_IOS thread_time_constraint_policy_data_t policy; policy.period = (uint32_t) (periodMs * 1000000); policy.computation = 50000; policy.constraint = policy.period; policy.preemptible = true; return thread_policy_set (pthread_mach_thread_np (thread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &policy, THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS; #else (void) periodMs; struct sched_param param; param.sched_priority = sched_get_priority_max (SCHED_RR); return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0; #endif } JUCE_DECLARE_NON_COPYABLE (Pimpl) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h000066400000000000000000000141331320201440200314460ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_WIN32_COMSMARTPTR_H_INCLUDED #define JUCE_WIN32_COMSMARTPTR_H_INCLUDED #if JUCE_MINGW && defined(__uuidof) #undef __uuidof #endif #if ! (defined (_MSC_VER) || defined (__uuidof)) template struct UUIDGetter { static CLSID get() { jassertfalse; return CLSID(); } }; #define __uuidof(x) UUIDGetter::get() #endif inline GUID uuidFromString (const char* const s) noexcept { unsigned long p0; unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; #ifndef _MSC_VER sscanf #else sscanf_s #endif (s, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10); GUID g = { p0, (uint16) p1, (uint16) p2, { (uint8) p3, (uint8) p4, (uint8) p5, (uint8) p6, (uint8) p7, (uint8) p8, (uint8) p9, (uint8) p10 }}; return g; } //============================================================================== /** A simple COM smart pointer. */ template class ComSmartPtr { public: ComSmartPtr() throw() : p (0) {} ComSmartPtr (ComClass* const obj) : p (obj) { if (p) p->AddRef(); } ComSmartPtr (const ComSmartPtr& other) : p (other.p) { if (p) p->AddRef(); } ~ComSmartPtr() { release(); } operator ComClass*() const throw() { return p; } ComClass& operator*() const throw() { return *p; } ComClass* operator->() const throw() { return p; } ComSmartPtr& operator= (ComClass* const newP) { if (newP != 0) newP->AddRef(); release(); p = newP; return *this; } ComSmartPtr& operator= (const ComSmartPtr& newP) { return operator= (newP.p); } // Releases and nullifies this pointer and returns its address ComClass** resetAndGetPointerAddress() { release(); p = 0; return &p; } HRESULT CoCreateInstance (REFCLSID classUUID, DWORD dwClsContext = CLSCTX_INPROC_SERVER) { HRESULT hr = ::CoCreateInstance (classUUID, 0, dwClsContext, __uuidof (ComClass), (void**) resetAndGetPointerAddress()); jassert (hr != CO_E_NOTINITIALIZED); // You haven't called CoInitialize for the current thread! return hr; } template HRESULT QueryInterface (REFCLSID classUUID, ComSmartPtr& destObject) const { if (p == 0) return E_POINTER; return p->QueryInterface (classUUID, (void**) destObject.resetAndGetPointerAddress()); } template HRESULT QueryInterface (ComSmartPtr& destObject) const { return this->QueryInterface (__uuidof (OtherComClass), destObject); } private: ComClass* p; void release() { if (p != 0) p->Release(); } ComClass** operator&() throw(); // private to avoid it being used accidentally }; //============================================================================== #define JUCE_COMRESULT HRESULT __stdcall //============================================================================== template class ComBaseClassHelperBase : public ComClass { public: ComBaseClassHelperBase (unsigned int initialRefCount) : refCount (initialRefCount) {} virtual ~ComBaseClassHelperBase() {} ULONG __stdcall AddRef() { return ++refCount; } ULONG __stdcall Release() { const ULONG r = --refCount; if (r == 0) delete this; return r; } protected: ULONG refCount; JUCE_COMRESULT QueryInterface (REFIID refId, void** result) { if (refId == IID_IUnknown) return castToType (result); *result = 0; return E_NOINTERFACE; } template JUCE_COMRESULT castToType (void** result) { this->AddRef(); *result = dynamic_cast (this); return S_OK; } }; /** Handy base class for writing COM objects, providing ref-counting and a basic QueryInterface method. */ template class ComBaseClassHelper : public ComBaseClassHelperBase { public: ComBaseClassHelper (unsigned int initialRefCount = 1) : ComBaseClassHelperBase (initialRefCount) {} ~ComBaseClassHelper() {} JUCE_COMRESULT QueryInterface (REFIID refId, void** result) { if (refId == __uuidof (ComClass)) return this->template castToType (result); return ComBaseClassHelperBase ::QueryInterface (refId, result); } }; #endif // JUCE_WIN32_COMSMARTPTR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp000066400000000000000000001000151320201440200306230ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD) -1) #endif //============================================================================== namespace WindowsFileHelpers { DWORD getAtts (const String& path) { return GetFileAttributes (path.toWideCharPointer()); } int64 fileTimeToTime (const FILETIME* const ft) { static_jassert (sizeof (ULARGE_INTEGER) == sizeof (FILETIME)); // tell me if this fails! return (int64) ((reinterpret_cast (ft)->QuadPart - 116444736000000000LL) / 10000); } FILETIME* timeToFileTime (const int64 time, FILETIME* const ft) noexcept { if (time <= 0) return nullptr; reinterpret_cast (ft)->QuadPart = (ULONGLONG) (time * 10000 + 116444736000000000LL); return ft; } String getDriveFromPath (String path) { if (path.isNotEmpty() && path[1] == ':' && path[2] == 0) path << '\\'; const size_t numBytes = CharPointer_UTF16::getBytesRequiredFor (path.getCharPointer()) + 4; HeapBlock pathCopy; pathCopy.calloc (numBytes, 1); path.copyToUTF16 (pathCopy, numBytes); if (PathStripToRoot (pathCopy)) path = static_cast (pathCopy); return path; } int64 getDiskSpaceInfo (const String& path, const bool total) { ULARGE_INTEGER spc, tot, totFree; if (GetDiskFreeSpaceEx (getDriveFromPath (path).toWideCharPointer(), &spc, &tot, &totFree)) return total ? (int64) tot.QuadPart : (int64) spc.QuadPart; return 0; } unsigned int getWindowsDriveType (const String& path) { return GetDriveType (getDriveFromPath (path).toWideCharPointer()); } File getSpecialFolderPath (int type) { WCHAR path [MAX_PATH + 256]; if (SHGetSpecialFolderPath (0, path, type, FALSE)) return File (String (path)); return File(); } File getModuleFileName (HINSTANCE moduleHandle) { WCHAR dest [MAX_PATH + 256]; dest[0] = 0; GetModuleFileName (moduleHandle, dest, (DWORD) numElementsInArray (dest)); return File (String (dest)); } Result getResultForLastError() { TCHAR messageBuffer [256] = { 0 }; FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, (DWORD) numElementsInArray (messageBuffer) - 1, nullptr); return Result::fail (String (messageBuffer)); } } //============================================================================== const juce_wchar File::separator = '\\'; const String File::separatorString ("\\"); //============================================================================== bool File::exists() const { return fullPath.isNotEmpty() && WindowsFileHelpers::getAtts (fullPath) != INVALID_FILE_ATTRIBUTES; } bool File::existsAsFile() const { return fullPath.isNotEmpty() && (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_DIRECTORY) == 0; } bool File::isDirectory() const { const DWORD attr = WindowsFileHelpers::getAtts (fullPath); return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 && attr != INVALID_FILE_ATTRIBUTES; } bool File::hasWriteAccess() const { if (fullPath.isEmpty()) return true; const DWORD attr = WindowsFileHelpers::getAtts (fullPath); // NB: According to MS, the FILE_ATTRIBUTE_READONLY attribute doesn't work for // folders, and can be incorrectly set for some special folders, so we'll just say // that folders are always writable. return attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 || (attr & FILE_ATTRIBUTE_READONLY) == 0; } bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const { const DWORD oldAtts = WindowsFileHelpers::getAtts (fullPath); if (oldAtts == INVALID_FILE_ATTRIBUTES) return false; const DWORD newAtts = shouldBeReadOnly ? (oldAtts | FILE_ATTRIBUTE_READONLY) : (oldAtts & ~FILE_ATTRIBUTE_READONLY); return newAtts == oldAtts || SetFileAttributes (fullPath.toWideCharPointer(), newAtts) != FALSE; } bool File::setFileExecutableInternal (bool /*shouldBeExecutable*/) const { // XXX is this possible? return false; } bool File::isHidden() const { return (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_HIDDEN) != 0; } //============================================================================== bool File::deleteFile() const { if (! exists()) return true; return isDirectory() ? RemoveDirectory (fullPath.toWideCharPointer()) != 0 : DeleteFile (fullPath.toWideCharPointer()) != 0; } bool File::moveToTrash() const { if (! exists()) return true; // The string we pass in must be double null terminated.. const size_t numBytes = CharPointer_UTF16::getBytesRequiredFor (fullPath.getCharPointer()) + 8; HeapBlock doubleNullTermPath; doubleNullTermPath.calloc (numBytes, 1); fullPath.copyToUTF16 (doubleNullTermPath, numBytes); SHFILEOPSTRUCT fos = { 0 }; fos.wFunc = FO_DELETE; fos.pFrom = doubleNullTermPath; fos.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_RENAMEONCOLLISION; return SHFileOperation (&fos) == 0; } bool File::copyInternal (const File& dest) const { return CopyFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer(), false) != 0; } bool File::moveInternal (const File& dest) const { return MoveFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer()) != 0; } Result File::createDirectoryInternal (const String& fileName) const { return CreateDirectory (fileName.toWideCharPointer(), 0) ? Result::ok() : WindowsFileHelpers::getResultForLastError(); } //============================================================================== int64 juce_fileSetPosition (void* handle, int64 pos) { LARGE_INTEGER li; li.QuadPart = pos; li.LowPart = SetFilePointer ((HANDLE) handle, (LONG) li.LowPart, &li.HighPart, FILE_BEGIN); // (returns -1 if it fails) return li.QuadPart; } void FileInputStream::openHandle() { HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); if (h != INVALID_HANDLE_VALUE) fileHandle = (void*) h; else status = WindowsFileHelpers::getResultForLastError(); } FileInputStream::~FileInputStream() { CloseHandle ((HANDLE) fileHandle); } size_t FileInputStream::readInternal (void* buffer, size_t numBytes) { if (fileHandle != 0) { DWORD actualNum = 0; if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0)) status = WindowsFileHelpers::getResultForLastError(); return (size_t) actualNum; } return 0; } //============================================================================== void FileOutputStream::openHandle() { HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (h != INVALID_HANDLE_VALUE) { LARGE_INTEGER li; li.QuadPart = 0; li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_END); if (li.LowPart != INVALID_SET_FILE_POINTER) { fileHandle = (void*) h; currentPosition = li.QuadPart; return; } } status = WindowsFileHelpers::getResultForLastError(); } void FileOutputStream::closeHandle() { CloseHandle ((HANDLE) fileHandle); } ssize_t FileOutputStream::writeInternal (const void* buffer, size_t numBytes) { if (fileHandle != nullptr) { DWORD actualNum = 0; if (! WriteFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0)) status = WindowsFileHelpers::getResultForLastError(); return (ssize_t) actualNum; } return 0; } void FileOutputStream::flushInternal() { if (fileHandle != nullptr) if (! FlushFileBuffers ((HANDLE) fileHandle)) status = WindowsFileHelpers::getResultForLastError(); } Result FileOutputStream::truncate() { if (fileHandle == nullptr) return status; flush(); return SetEndOfFile ((HANDLE) fileHandle) ? Result::ok() : WindowsFileHelpers::getResultForLastError(); } //============================================================================== void MemoryMappedFile::openInternal (const File& file, AccessMode mode) { jassert (mode == readOnly || mode == readWrite); if (range.getStart() > 0) { SYSTEM_INFO systemInfo; GetNativeSystemInfo (&systemInfo); range.setStart (range.getStart() - (range.getStart() % systemInfo.dwAllocationGranularity)); } DWORD accessMode = GENERIC_READ, createType = OPEN_EXISTING; DWORD protect = PAGE_READONLY, access = FILE_MAP_READ; if (mode == readWrite) { accessMode = GENERIC_READ | GENERIC_WRITE; createType = OPEN_ALWAYS; protect = PAGE_READWRITE; access = FILE_MAP_ALL_ACCESS; } HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode, FILE_SHARE_READ, 0, createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); if (h != INVALID_HANDLE_VALUE) { fileHandle = (void*) h; HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (range.getEnd() >> 32), (DWORD) range.getEnd(), 0); if (mappingHandle != 0) { address = MapViewOfFile (mappingHandle, access, (DWORD) (range.getStart() >> 32), (DWORD) range.getStart(), (SIZE_T) range.getLength()); if (address == nullptr) range = Range(); CloseHandle (mappingHandle); } } } MemoryMappedFile::~MemoryMappedFile() { if (address != nullptr) UnmapViewOfFile (address); if (fileHandle != nullptr) CloseHandle ((HANDLE) fileHandle); } //============================================================================== int64 File::getSize() const { WIN32_FILE_ATTRIBUTE_DATA attributes; if (GetFileAttributesEx (fullPath.toWideCharPointer(), GetFileExInfoStandard, &attributes)) return (((int64) attributes.nFileSizeHigh) << 32) | attributes.nFileSizeLow; return 0; } void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const { using namespace WindowsFileHelpers; WIN32_FILE_ATTRIBUTE_DATA attributes; if (GetFileAttributesEx (fullPath.toWideCharPointer(), GetFileExInfoStandard, &attributes)) { modificationTime = fileTimeToTime (&attributes.ftLastWriteTime); creationTime = fileTimeToTime (&attributes.ftCreationTime); accessTime = fileTimeToTime (&attributes.ftLastAccessTime); } else { creationTime = accessTime = modificationTime = 0; } } bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 creationTime) const { using namespace WindowsFileHelpers; bool ok = false; HANDLE h = CreateFile (fullPath.toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (h != INVALID_HANDLE_VALUE) { FILETIME m, a, c; ok = SetFileTime (h, timeToFileTime (creationTime, &c), timeToFileTime (accessTime, &a), timeToFileTime (modificationTime, &m)) != 0; CloseHandle (h); } return ok; } //============================================================================== void File::findFileSystemRoots (Array& destArray) { TCHAR buffer [2048] = { 0 }; GetLogicalDriveStrings (2048, buffer); const TCHAR* n = buffer; StringArray roots; while (*n != 0) { roots.add (String (n)); while (*n++ != 0) {} } roots.sort (true); for (int i = 0; i < roots.size(); ++i) destArray.add (roots [i]); } //============================================================================== String File::getVolumeLabel() const { TCHAR dest[64]; if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest, (DWORD) numElementsInArray (dest), 0, 0, 0, 0, 0)) dest[0] = 0; return dest; } int File::getVolumeSerialNumber() const { TCHAR dest[64]; DWORD serialNum; if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest, (DWORD) numElementsInArray (dest), &serialNum, 0, 0, 0, 0)) return 0; return (int) serialNum; } int64 File::getBytesFreeOnVolume() const { return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), false); } int64 File::getVolumeTotalSize() const { return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), true); } uint64 File::getFileIdentifier() const { uint64 result = 0; HANDLE h = CreateFile (getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); if (h != INVALID_HANDLE_VALUE) { BY_HANDLE_FILE_INFORMATION info; zerostruct (info); if (GetFileInformationByHandle (h, &info)) result = (((uint64) info.nFileIndexHigh) << 32) | info.nFileIndexLow; CloseHandle (h); } return result; } //============================================================================== bool File::isOnCDRomDrive() const { return WindowsFileHelpers::getWindowsDriveType (getFullPathName()) == DRIVE_CDROM; } bool File::isOnHardDisk() const { if (fullPath.isEmpty()) return false; const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName()); if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':') return n != DRIVE_REMOVABLE; return n != DRIVE_CDROM && n != DRIVE_REMOTE; } bool File::isOnRemovableDrive() const { if (fullPath.isEmpty()) return false; const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName()); return n == DRIVE_CDROM || n == DRIVE_REMOTE || n == DRIVE_REMOVABLE || n == DRIVE_RAMDISK; } //============================================================================== File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) { int csidlType = 0; switch (type) { case userHomeDirectory: csidlType = CSIDL_PROFILE; break; case userDocumentsDirectory: csidlType = CSIDL_PERSONAL; break; case userDesktopDirectory: csidlType = CSIDL_DESKTOP; break; case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break; case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break; case commonDocumentsDirectory: csidlType = CSIDL_COMMON_DOCUMENTS; break; case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break; case userMusicDirectory: csidlType = 0x0d; /*CSIDL_MYMUSIC*/ break; case userMoviesDirectory: csidlType = 0x0e; /*CSIDL_MYVIDEO*/ break; case userPicturesDirectory: csidlType = 0x27; /*CSIDL_MYPICTURES*/ break; case tempDirectory: { WCHAR dest [2048]; dest[0] = 0; GetTempPath ((DWORD) numElementsInArray (dest), dest); return File (String (dest)); } case windowsSystemDirectory: { WCHAR dest [2048]; dest[0] = 0; GetSystemDirectoryW (dest, (UINT) numElementsInArray (dest)); return File (String (dest)); } case invokedExecutableFile: case currentExecutableFile: case currentApplicationFile: return WindowsFileHelpers::getModuleFileName ((HINSTANCE) Process::getCurrentModuleInstanceHandle()); case hostApplicationPath: return WindowsFileHelpers::getModuleFileName (0); default: jassertfalse; // unknown type? return File(); } return WindowsFileHelpers::getSpecialFolderPath (csidlType); } //============================================================================== File File::getCurrentWorkingDirectory() { WCHAR dest [MAX_PATH + 256]; dest[0] = 0; GetCurrentDirectory ((DWORD) numElementsInArray (dest), dest); return File (String (dest)); } bool File::setAsCurrentWorkingDirectory() const { return SetCurrentDirectory (getFullPathName().toWideCharPointer()) != FALSE; } //============================================================================== String File::getVersion() const { String result; DWORD handle = 0; DWORD bufferSize = GetFileVersionInfoSize (getFullPathName().toWideCharPointer(), &handle); HeapBlock buffer; buffer.calloc (bufferSize); if (GetFileVersionInfo (getFullPathName().toWideCharPointer(), 0, bufferSize, buffer)) { VS_FIXEDFILEINFO* vffi; UINT len = 0; if (VerQueryValue (buffer, (LPTSTR) _T("\\"), (LPVOID*) &vffi, &len)) { result << (int) HIWORD (vffi->dwFileVersionMS) << '.' << (int) LOWORD (vffi->dwFileVersionMS) << '.' << (int) HIWORD (vffi->dwFileVersionLS) << '.' << (int) LOWORD (vffi->dwFileVersionLS); } } return result; } //============================================================================== bool File::isLink() const { return hasFileExtension (".lnk"); } File File::getLinkedTarget() const { File result (*this); String p (getFullPathName()); if (! exists()) p += ".lnk"; else if (! hasFileExtension (".lnk")) return result; ComSmartPtr shellLink; ComSmartPtr persistFile; if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) && SUCCEEDED (shellLink.QueryInterface (persistFile)) && SUCCEEDED (persistFile->Load (p.toWideCharPointer(), STGM_READ)) && SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))) { WIN32_FIND_DATA winFindData; WCHAR resolvedPath [MAX_PATH]; if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, SLGP_UNCPRIORITY))) result = File (resolvedPath); } return result; } bool File::createLink (const String& description, const File& linkFileToCreate) const { linkFileToCreate.deleteFile(); ComSmartPtr shellLink; ComSmartPtr persistFile; CoInitialize (0); return SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) && SUCCEEDED (shellLink->SetPath (getFullPathName().toWideCharPointer())) && SUCCEEDED (shellLink->SetDescription (description.toWideCharPointer())) && SUCCEEDED (shellLink.QueryInterface (persistFile)) && SUCCEEDED (persistFile->Save (linkFileToCreate.getFullPathName().toWideCharPointer(), TRUE)); } //============================================================================== class DirectoryIterator::NativeIterator::Pimpl { public: Pimpl (const File& directory, const String& wildCard) : directoryWithWildCard (File::addTrailingSeparator (directory.getFullPathName()) + wildCard), handle (INVALID_HANDLE_VALUE) { } ~Pimpl() { if (handle != INVALID_HANDLE_VALUE) FindClose (handle); } bool next (String& filenameFound, bool* const isDir, bool* const isHidden, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { using namespace WindowsFileHelpers; WIN32_FIND_DATA findData; if (handle == INVALID_HANDLE_VALUE) { handle = FindFirstFile (directoryWithWildCard.toWideCharPointer(), &findData); if (handle == INVALID_HANDLE_VALUE) return false; } else { if (FindNextFile (handle, &findData) == 0) return false; } filenameFound = findData.cFileName; if (isDir != nullptr) *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); if (isHidden != nullptr) *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); if (isReadOnly != nullptr) *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); if (fileSize != nullptr) *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32); if (modTime != nullptr) *modTime = Time (fileTimeToTime (&findData.ftLastWriteTime)); if (creationTime != nullptr) *creationTime = Time (fileTimeToTime (&findData.ftCreationTime)); return true; } private: const String directoryWithWildCard; HANDLE handle; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) { } DirectoryIterator::NativeIterator::~NativeIterator() { } bool DirectoryIterator::NativeIterator::next (String& filenameFound, bool* const isDir, bool* const isHidden, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } //============================================================================== bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { HINSTANCE hInstance = 0; JUCE_TRY { hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT); } JUCE_CATCH_ALL return hInstance > (HINSTANCE) 32; } void File::revealToUser() const { DynamicLibrary dll ("Shell32.dll"); JUCE_LOAD_WINAPI_FUNCTION (dll, ILCreateFromPathW, ilCreateFromPathW, ITEMIDLIST*, (LPCWSTR)) JUCE_LOAD_WINAPI_FUNCTION (dll, ILFree, ilFree, void, (ITEMIDLIST*)) JUCE_LOAD_WINAPI_FUNCTION (dll, SHOpenFolderAndSelectItems, shOpenFolderAndSelectItems, HRESULT, (ITEMIDLIST*, UINT, void*, DWORD)) if (ilCreateFromPathW != nullptr && shOpenFolderAndSelectItems != nullptr && ilFree != nullptr) { if (ITEMIDLIST* const itemIDList = ilCreateFromPathW (fullPath.toWideCharPointer())) { shOpenFolderAndSelectItems (itemIDList, 0, nullptr, 0); ilFree (itemIDList); } } } //============================================================================== class NamedPipe::Pimpl { public: Pimpl (const String& pipeName, const bool createPipe) : filename ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName)), pipeH (INVALID_HANDLE_VALUE), cancelEvent (CreateEvent (0, FALSE, FALSE, 0)), connected (false), ownsPipe (createPipe), shouldStop (false) { if (createPipe) { pipeH = CreateNamedPipe (filename.toWideCharPointer(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0); if (GetLastError() == ERROR_ALREADY_EXISTS) closePipeHandle(); } } ~Pimpl() { closePipeHandle(); CloseHandle (cancelEvent); } bool connect (const int timeOutMs) { if (! ownsPipe) { if (pipeH != INVALID_HANDLE_VALUE) return true; const Time timeOutEnd (Time::getCurrentTime() + RelativeTime::milliseconds (timeOutMs)); for (;;) { { const ScopedLock sl (createFileLock); if (pipeH == INVALID_HANDLE_VALUE) pipeH = CreateFile (filename.toWideCharPointer(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); } if (pipeH != INVALID_HANDLE_VALUE) return true; if (shouldStop || (timeOutMs >= 0 && Time::getCurrentTime() > timeOutEnd)) return false; Thread::sleep (1); } } if (! connected) { OverlappedEvent over; if (ConnectNamedPipe (pipeH, &over.over) == 0) { switch (GetLastError()) { case ERROR_PIPE_CONNECTED: connected = true; break; case ERROR_IO_PENDING: case ERROR_PIPE_LISTENING: connected = waitForIO (over, timeOutMs); break; default: break; } } } return connected; } void disconnectPipe() { if (ownsPipe && connected) { DisconnectNamedPipe (pipeH); connected = false; } } void closePipeHandle() { if (pipeH != INVALID_HANDLE_VALUE) { disconnectPipe(); CloseHandle (pipeH); pipeH = INVALID_HANDLE_VALUE; } } int read (void* destBuffer, const int maxBytesToRead, const int timeOutMilliseconds) { while (connect (timeOutMilliseconds)) { if (maxBytesToRead <= 0) return 0; OverlappedEvent over; unsigned long numRead; if (ReadFile (pipeH, destBuffer, (DWORD) maxBytesToRead, &numRead, &over.over)) return (int) numRead; const DWORD lastError = GetLastError(); if (lastError == ERROR_IO_PENDING) { if (! waitForIO (over, timeOutMilliseconds)) return -1; if (GetOverlappedResult (pipeH, &over.over, &numRead, FALSE)) return (int) numRead; } if (ownsPipe && (GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_PIPE_NOT_CONNECTED)) disconnectPipe(); else break; } return -1; } int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { if (connect (timeOutMilliseconds)) { if (numBytesToWrite <= 0) return 0; OverlappedEvent over; unsigned long numWritten; if (WriteFile (pipeH, sourceBuffer, (DWORD) numBytesToWrite, &numWritten, &over.over)) return (int) numWritten; if (GetLastError() == ERROR_IO_PENDING) { if (! waitForIO (over, timeOutMilliseconds)) return -1; if (GetOverlappedResult (pipeH, &over.over, &numWritten, FALSE)) return (int) numWritten; if (GetLastError() == ERROR_BROKEN_PIPE && ownsPipe) disconnectPipe(); } } return -1; } const String filename; HANDLE pipeH, cancelEvent; bool connected, ownsPipe, shouldStop; CriticalSection createFileLock; private: struct OverlappedEvent { OverlappedEvent() { zerostruct (over); over.hEvent = CreateEvent (0, TRUE, FALSE, 0); } ~OverlappedEvent() { CloseHandle (over.hEvent); } OVERLAPPED over; }; bool waitForIO (OverlappedEvent& over, int timeOutMilliseconds) { if (shouldStop) return false; HANDLE handles[] = { over.over.hEvent, cancelEvent }; DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE, timeOutMilliseconds >= 0 ? timeOutMilliseconds : INFINITE); if (waitResult == WAIT_OBJECT_0) return true; CancelIo (pipeH); return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; void NamedPipe::close() { if (pimpl != nullptr) { pimpl->shouldStop = true; SetEvent (pimpl->cancelEvent); ScopedWriteLock sl (lock); pimpl = nullptr; } } bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) { pimpl = new Pimpl (pipeName, createPipe); if (createPipe && pimpl->pipeH == INVALID_HANDLE_VALUE) { pimpl = nullptr; return false; } return true; } int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { ScopedReadLock sl (lock); return pimpl != nullptr ? pimpl->read (destBuffer, maxBytesToRead, timeOutMilliseconds) : -1; } int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { ScopedReadLock sl (lock); return pimpl != nullptr ? pimpl->write (sourceBuffer, numBytesToWrite, timeOutMilliseconds) : -1; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp000066400000000000000000000454001320201440200312200ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef INTERNET_FLAG_NEED_FILE #define INTERNET_FLAG_NEED_FILE 0x00000010 #endif #ifndef INTERNET_OPTION_DISABLE_AUTODIAL #define INTERNET_OPTION_DISABLE_AUTODIAL 70 #endif //============================================================================== class WebInputStream : public InputStream { public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, int numRedirectsToFollow) : statusCode (0), connection (0), request (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { while (numRedirectsToFollow-- >= 0) { createConnection (progressCallback, progressCallbackContext); if (! isError()) { DWORD bufferSizeBytes = 4096; StringPairArray headers (false); for (;;) { HeapBlock buffer ((size_t) bufferSizeBytes); if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) { StringArray headersArray; headersArray.addLines (String (reinterpret_cast (buffer.getData()))); for (int i = 0; i < headersArray.size(); ++i) { const String& header = headersArray[i]; const String key (header.upToFirstOccurrenceOf (": ", false, false)); const String value (header.fromFirstOccurrenceOf (": ", false, false)); const String previousValue (headers[key]); headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } break; } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break; bufferSizeBytes += 4096; } DWORD status = 0; DWORD statusSize = sizeof (status); if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0)) { statusCode = (int) status; if (numRedirectsToFollow >= 0 && (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307)) { String newLocation (headers["Location"]); // Check whether location is a relative URI - this is an incomplete test for relative path, // but we'll use it for now (valid protocols for this implementation are http, https & ftp) if (! (newLocation.startsWithIgnoreCase ("http://") || newLocation.startsWithIgnoreCase ("https://") || newLocation.startsWithIgnoreCase ("ftp://"))) { if (newLocation.startsWithChar ('/')) newLocation = URL (address).withNewSubPath (newLocation).toString (true); else newLocation = address + "/" + newLocation; } if (newLocation.isNotEmpty() && newLocation != address) { address = newLocation; continue; } } } if (responseHeaders != nullptr) responseHeaders->addArray (headers); } break; } } ~WebInputStream() { close(); } //============================================================================== bool isError() const { return request == 0; } bool isExhausted() { return finished; } int64 getPosition() { return position; } int64 getTotalLength() { if (! isError()) { DWORD index = 0, result = 0, size = sizeof (result); if (HttpQueryInfo (request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index)) return (int64) result; } return -1; } int read (void* buffer, int bytesToRead) { jassert (buffer != nullptr && bytesToRead >= 0); DWORD bytesRead = 0; if (! (finished || isError())) { InternetReadFile (request, buffer, (DWORD) bytesToRead, &bytesRead); position += bytesRead; if (bytesRead == 0) finished = true; } return (int) bytesRead; } bool setPosition (int64 wantedPos) { if (isError()) return false; if (wantedPos != position) { finished = false; position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, 0, FILE_BEGIN, 0); if (position == wantedPos) return true; if (wantedPos < position) { close(); position = 0; createConnection (0, 0); } skipNextBytes (wantedPos - position); } return true; } int statusCode; private: //============================================================================== HINTERNET connection, request; String address, headers; MemoryBlock postData; int64 position; bool finished; const bool isPost; int timeOutMs; void close() { if (request != 0) { InternetCloseHandle (request); request = 0; } if (connection != 0) { InternetCloseHandle (connection); connection = 0; } } void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0); close(); if (sessionHandle != 0) { // break up the url.. const int fileNumChars = 65536; const int serverNumChars = 2048; const int usernameNumChars = 1024; const int passwordNumChars = 1024; HeapBlock file (fileNumChars), server (serverNumChars), username (usernameNumChars), password (passwordNumChars); URL_COMPONENTS uc = { 0 }; uc.dwStructSize = sizeof (uc); uc.lpszUrlPath = file; uc.dwUrlPathLength = fileNumChars; uc.lpszHostName = server; uc.dwHostNameLength = serverNumChars; uc.lpszUserName = username; uc.dwUserNameLength = usernameNumChars; uc.lpszPassword = password; uc.dwPasswordLength = passwordNumChars; if (InternetCrackUrl (address.toWideCharPointer(), 0, 0, &uc)) openConnection (uc, sessionHandle, progressCallback, progressCallbackContext); } } void openConnection (URL_COMPONENTS& uc, HINTERNET sessionHandle, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { int disable = 1; InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable)); if (timeOutMs == 0) timeOutMs = 30000; else if (timeOutMs < 0) timeOutMs = -1; applyTimeout (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT); applyTimeout (sessionHandle, INTERNET_OPTION_RECEIVE_TIMEOUT); applyTimeout (sessionHandle, INTERNET_OPTION_SEND_TIMEOUT); applyTimeout (sessionHandle, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT); applyTimeout (sessionHandle, INTERNET_OPTION_DATA_SEND_TIMEOUT); const bool isFtp = address.startsWithIgnoreCase ("ftp:"); connection = InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort, uc.lpszUserName, uc.lpszPassword, isFtp ? (DWORD) INTERNET_SERVICE_FTP : (DWORD) INTERNET_SERVICE_HTTP, 0, 0); if (connection != 0) { if (isFtp) request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ, FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, 0); else openHTTPConnection (uc, progressCallback, progressCallbackContext); } } void applyTimeout (HINTERNET sessionHandle, const DWORD option) { InternetSetOption (sessionHandle, option, &timeOutMs, sizeof (timeOutMs)); } void openHTTPConnection (URL_COMPONENTS& uc, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { const TCHAR* mimeTypes[] = { _T("*/*"), nullptr }; DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_AUTO_REDIRECT | SECURITY_SET_MASK; if (address.startsWithIgnoreCase ("https:")) flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 - // IE7 seems to automatically work out when it's https) request = HttpOpenRequest (connection, isPost ? _T("POST") : _T("GET"), uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0); if (request != 0) { setSecurityFlags(); INTERNET_BUFFERS buffers = { 0 }; buffers.dwStructSize = sizeof (INTERNET_BUFFERS); buffers.lpcszHeader = headers.toWideCharPointer(); buffers.dwHeadersLength = (DWORD) headers.length(); buffers.dwBufferTotal = (DWORD) postData.getSize(); if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) { int bytesSent = 0; for (;;) { const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent); DWORD bytesDone = 0; if (bytesToDo > 0 && ! InternetWriteFile (request, static_cast (postData.getData()) + bytesSent, (DWORD) bytesToDo, &bytesDone)) { break; } if (bytesToDo == 0 || (int) bytesDone < bytesToDo) { if (HttpEndRequest (request, 0, 0, 0)) return; break; } bytesSent += bytesDone; if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, bytesSent, (int) postData.getSize())) break; } } } close(); } void setSecurityFlags() { DWORD dwFlags = 0, dwBuffLen = sizeof (DWORD); InternetQueryOption (request, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, &dwBuffLen); dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_SET_MASK; InternetSetOption (request, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags)); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; //============================================================================== struct GetAdaptersInfoHelper { bool callGetAdaptersInfo() { DynamicLibrary dll ("iphlpapi.dll"); JUCE_LOAD_WINAPI_FUNCTION (dll, GetAdaptersInfo, getAdaptersInfo, DWORD, (PIP_ADAPTER_INFO, PULONG)) if (getAdaptersInfo == nullptr) return false; adapterInfo.malloc (1); ULONG len = sizeof (IP_ADAPTER_INFO); if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW) adapterInfo.malloc (len, 1); return getAdaptersInfo (adapterInfo, &len) == NO_ERROR; } HeapBlock adapterInfo; }; namespace MACAddressHelpers { static void addAddress (Array& result, const MACAddress& ma) { if (! ma.isNull()) result.addIfNotAlreadyThere (ma); } static void getViaGetAdaptersInfo (Array& result) { GetAdaptersInfoHelper gah; if (gah.callGetAdaptersInfo()) { for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) if (adapter->AddressLength >= 6) addAddress (result, MACAddress (adapter->Address)); } } static void getViaNetBios (Array& result) { DynamicLibrary dll ("netapi32.dll"); JUCE_LOAD_WINAPI_FUNCTION (dll, Netbios, NetbiosCall, UCHAR, (PNCB)) if (NetbiosCall != 0) { LANA_ENUM enums = { 0 }; { NCB ncb = { 0 }; ncb.ncb_command = NCBENUM; ncb.ncb_buffer = (unsigned char*) &enums; ncb.ncb_length = sizeof (LANA_ENUM); NetbiosCall (&ncb); } for (int i = 0; i < enums.length; ++i) { NCB ncb2 = { 0 }; ncb2.ncb_command = NCBRESET; ncb2.ncb_lana_num = enums.lana[i]; if (NetbiosCall (&ncb2) == 0) { NCB ncb = { 0 }; memcpy (ncb.ncb_callname, "* ", NCBNAMSZ); ncb.ncb_command = NCBASTAT; ncb.ncb_lana_num = enums.lana[i]; struct ASTAT { ADAPTER_STATUS adapt; NAME_BUFFER NameBuff [30]; }; ASTAT astat; zerostruct (astat); ncb.ncb_buffer = (unsigned char*) &astat; ncb.ncb_length = sizeof (ASTAT); if (NetbiosCall (&ncb) == 0 && astat.adapt.adapter_type == 0xfe) addAddress (result, MACAddress (astat.adapt.adapter_address)); } } } } } void MACAddress::findAllAddresses (Array& result) { MACAddressHelpers::getViaGetAdaptersInfo (result); MACAddressHelpers::getViaNetBios (result); } void IPAddress::findAllAddresses (Array& result) { result.addIfNotAlreadyThere (IPAddress::local()); GetAdaptersInfoHelper gah; if (gah.callGetAdaptersInfo()) { for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) { IPAddress ip (adapter->IpAddressList.IpAddress.String); if (ip != IPAddress::any()) result.addIfNotAlreadyThere (ip); } } } //============================================================================== bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, const String& emailSubject, const String& bodyText, const StringArray& filesToAttach) { DynamicLibrary dll ("MAPI32.dll"); JUCE_LOAD_WINAPI_FUNCTION (dll, MAPISendMail, mapiSendMail, ULONG, (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG)) if (mapiSendMail == nullptr) return false; MapiMessage message = { 0 }; message.lpszSubject = (LPSTR) emailSubject.toRawUTF8(); message.lpszNoteText = (LPSTR) bodyText.toRawUTF8(); MapiRecipDesc recip = { 0 }; recip.ulRecipClass = MAPI_TO; String targetEmailAddress_ (targetEmailAddress); if (targetEmailAddress_.isEmpty()) targetEmailAddress_ = " "; // (Windows Mail can't deal with a blank address) recip.lpszName = (LPSTR) targetEmailAddress_.toRawUTF8(); message.nRecipCount = 1; message.lpRecips = &recip; HeapBlock files; files.calloc ((size_t) filesToAttach.size()); message.nFileCount = (ULONG) filesToAttach.size(); message.lpFiles = files; for (int i = 0; i < filesToAttach.size(); ++i) { files[i].nPosition = (ULONG) -1; files[i].lpszPathName = (LPSTR) filesToAttach[i].toRawUTF8(); } return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp000066400000000000000000000225771320201440200314110ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ struct RegistryKeyWrapper { RegistryKeyWrapper (String name, const bool createForWriting, const DWORD wow64Flags) : key (0), wideCharValueName (nullptr) { HKEY rootKey = 0; if (name.startsWithIgnoreCase ("HKEY_CURRENT_USER\\")) rootKey = HKEY_CURRENT_USER; else if (name.startsWithIgnoreCase ("HKEY_LOCAL_MACHINE\\")) rootKey = HKEY_LOCAL_MACHINE; else if (name.startsWithIgnoreCase ("HKEY_CLASSES_ROOT\\")) rootKey = HKEY_CLASSES_ROOT; if (rootKey != 0) { name = name.substring (name.indexOfChar ('\\') + 1); const int lastSlash = name.lastIndexOfChar ('\\'); valueName = name.substring (lastSlash + 1); wideCharValueName = valueName.toWideCharPointer(); name = name.substring (0, lastSlash); const wchar_t* const wideCharName = name.toWideCharPointer(); DWORD result; if (createForWriting) RegCreateKeyEx (rootKey, wideCharName, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, 0, &key, &result); else RegOpenKeyEx (rootKey, wideCharName, 0, KEY_READ | wow64Flags, &key); } } ~RegistryKeyWrapper() { if (key != 0) RegCloseKey (key); } static bool setValue (const String& regValuePath, const DWORD type, const void* data, size_t dataSize, const DWORD wow64Flags) { const RegistryKeyWrapper key (regValuePath, true, wow64Flags); return key.key != 0 && RegSetValueEx (key.key, key.wideCharValueName, 0, type, reinterpret_cast (data), (DWORD) dataSize) == ERROR_SUCCESS; } static uint32 getBinaryValue (const String& regValuePath, MemoryBlock& result, DWORD wow64Flags) { const RegistryKeyWrapper key (regValuePath, false, wow64Flags); if (key.key != 0) { for (unsigned long bufferSize = 1024; ; bufferSize *= 2) { result.setSize (bufferSize, false); DWORD type = REG_NONE; const LONG err = RegQueryValueEx (key.key, key.wideCharValueName, 0, &type, (LPBYTE) result.getData(), &bufferSize); if (err == ERROR_SUCCESS) { result.setSize (bufferSize, false); return type; } if (err != ERROR_MORE_DATA) break; } } return REG_NONE; } static String getValue (const String& regValuePath, const String& defaultValue, DWORD wow64Flags) { MemoryBlock buffer; switch (getBinaryValue (regValuePath, buffer, wow64Flags)) { case REG_SZ: return static_cast (buffer.getData()); case REG_DWORD: return String ((int) *reinterpret_cast (buffer.getData())); default: break; } return defaultValue; } static bool keyExists (const String& regValuePath, const DWORD wow64Flags) { return RegistryKeyWrapper (regValuePath, false, wow64Flags).key != 0; } static bool valueExists (const String& regValuePath, const DWORD wow64Flags) { const RegistryKeyWrapper key (regValuePath, false, wow64Flags); if (key.key == 0) return false; unsigned char buffer [512]; unsigned long bufferSize = sizeof (buffer); DWORD type = 0; const LONG result = RegQueryValueEx (key.key, key.wideCharValueName, 0, &type, buffer, &bufferSize); return result == ERROR_SUCCESS || result == ERROR_MORE_DATA; } HKEY key; const wchar_t* wideCharValueName; String valueName; JUCE_DECLARE_NON_COPYABLE (RegistryKeyWrapper) }; uint32 JUCE_CALLTYPE WindowsRegistry::getBinaryValue (const String& regValuePath, MemoryBlock& result, WoW64Mode mode) { return RegistryKeyWrapper::getBinaryValue (regValuePath, result, (DWORD) mode); } String JUCE_CALLTYPE WindowsRegistry::getValue (const String& regValuePath, const String& defaultValue, WoW64Mode mode) { return RegistryKeyWrapper::getValue (regValuePath, defaultValue, (DWORD) mode); } bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const String& value, WoW64Mode mode) { return RegistryKeyWrapper::setValue (regValuePath, REG_SZ, value.toWideCharPointer(), CharPointer_UTF16::getBytesRequiredFor (value.getCharPointer()), mode); } bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint32 value, WoW64Mode mode) { return RegistryKeyWrapper::setValue (regValuePath, REG_DWORD, &value, sizeof (value), (DWORD) mode); } bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint64 value, WoW64Mode mode) { return RegistryKeyWrapper::setValue (regValuePath, REG_QWORD, &value, sizeof (value), (DWORD) mode); } bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode) { return RegistryKeyWrapper::setValue (regValuePath, REG_BINARY, value.getData(), value.getSize(), (DWORD) mode); } bool JUCE_CALLTYPE WindowsRegistry::valueExists (const String& regValuePath, WoW64Mode mode) { return RegistryKeyWrapper::valueExists (regValuePath, (DWORD) mode); } bool JUCE_CALLTYPE WindowsRegistry::keyExists (const String& regValuePath, WoW64Mode mode) { return RegistryKeyWrapper::keyExists (regValuePath, (DWORD) mode); } void JUCE_CALLTYPE WindowsRegistry::deleteValue (const String& regValuePath, WoW64Mode mode) { const RegistryKeyWrapper key (regValuePath, true, (DWORD) mode); if (key.key != 0) RegDeleteValue (key.key, key.wideCharValueName); } void JUCE_CALLTYPE WindowsRegistry::deleteKey (const String& regKeyPath, WoW64Mode mode) { const RegistryKeyWrapper key (regKeyPath, true, (DWORD) mode); if (key.key != 0) RegDeleteKey (key.key, key.wideCharValueName); } bool JUCE_CALLTYPE WindowsRegistry::registerFileAssociation (const String& fileExtension, const String& symbolicDescription, const String& fullDescription, const File& targetExecutable, const int iconResourceNumber, const bool registerForCurrentUserOnly, WoW64Mode mode) { const char* const root = registerForCurrentUserOnly ? "HKEY_CURRENT_USER\\Software\\Classes\\" : "HKEY_CLASSES_ROOT\\"; const String key (root + symbolicDescription); return setValue (root + fileExtension + "\\", symbolicDescription, mode) && setValue (key + "\\", fullDescription, mode) && setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"", mode) && (iconResourceNumber == 0 || setValue (key + "\\DefaultIcon\\", targetExecutable.getFullPathName() + "," + String (iconResourceNumber))); } // These methods are deprecated: String WindowsRegistry::getValueWow64 (const String& p, const String& defVal) { return getValue (p, defVal, WoW64_64bit); } bool WindowsRegistry::valueExistsWow64 (const String& p) { return valueExists (p, WoW64_64bit); } bool WindowsRegistry::keyExistsWow64 (const String& p) { return keyExists (p, WoW64_64bit); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp000066400000000000000000000354371320201440200321030ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ void Logger::outputDebugString (const String& text) { OutputDebugString ((text + "\n").toWideCharPointer()); } //============================================================================== #ifdef JUCE_DLL_BUILD JUCE_API void* juceDLL_malloc (size_t sz) { return std::malloc (sz); } JUCE_API void juceDLL_free (void* block) { std::free (block); } #endif //============================================================================== #if JUCE_USE_MSVC_INTRINSICS // CPU info functions using intrinsics... #pragma intrinsic (__cpuid) #pragma intrinsic (__rdtsc) static void callCPUID (int result[4], int infoType) { __cpuid (result, infoType); } #else static void callCPUID (int result[4], int infoType) { #if ! JUCE_MINGW __try #endif { #if JUCE_GCC __asm__ __volatile__ ("cpuid" : "=a" (result[0]), "=b" (result[1]), "=c" (result[2]),"=d" (result[3]) : "a" (infoType)); #else __asm { mov esi, result mov eax, infoType xor ecx, ecx cpuid mov dword ptr [esi + 0], eax mov dword ptr [esi + 4], ebx mov dword ptr [esi + 8], ecx mov dword ptr [esi + 12], edx } #endif } #if ! JUCE_MINGW __except (EXCEPTION_EXECUTE_HANDLER) {} #endif } #endif String SystemStats::getCpuVendor() { int info[4] = { 0 }; callCPUID (info, 0); char v [12]; memcpy (v, info + 1, 4); memcpy (v + 4, info + 3, 4); memcpy (v + 8, info + 2, 4); return String (v, 12); } //============================================================================== void CPUInformation::initialise() noexcept { int info[4] = { 0 }; callCPUID (info, 1); // NB: IsProcessorFeaturePresent doesn't work on XP hasMMX = (info[3] & (1 << 23)) != 0; hasSSE = (info[3] & (1 << 25)) != 0; hasSSE2 = (info[3] & (1 << 26)) != 0; hasSSE3 = (info[2] & (1 << 0)) != 0; hasAVX = (info[2] & (1 << 28)) != 0; hasSSSE3 = (info[2] & (1 << 9)) != 0; has3DNow = (info[1] & (1 << 31)) != 0; SYSTEM_INFO systemInfo; GetNativeSystemInfo (&systemInfo); numCpus = (int) systemInfo.dwNumberOfProcessors; } #if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS struct DebugFlagsInitialiser { DebugFlagsInitialiser() { _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); } }; static DebugFlagsInitialiser debugFlagsInitialiser; #endif //============================================================================== static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) { OSVERSIONINFOEX info; zerostruct (info); info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); if (target >= SystemStats::Windows10) { info.dwMajorVersion = 10; info.dwMinorVersion = 0; } else if (target >= SystemStats::WinVista) { info.dwMajorVersion = 6; switch (target) { case SystemStats::WinVista: break; case SystemStats::Windows7: info.dwMinorVersion = 1; break; case SystemStats::Windows8_0: info.dwMinorVersion = 2; break; case SystemStats::Windows8_1: info.dwMinorVersion = 3; break; default: jassertfalse; break; } } else { info.dwMajorVersion = 5; info.dwMinorVersion = target >= SystemStats::WinXP ? 1 : 0; } DWORDLONG mask = 0; VER_SET_CONDITION (mask, VER_MAJORVERSION, VER_GREATER_EQUAL); VER_SET_CONDITION (mask, VER_MINORVERSION, VER_GREATER_EQUAL); VER_SET_CONDITION (mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); VER_SET_CONDITION (mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); return VerifyVersionInfo (&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, mask) != FALSE; } SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { const SystemStats::OperatingSystemType types[] = { Windows10, Windows8_1, Windows8_0, Windows7, WinVista, WinXP, Win2000 }; for (int i = 0; i < numElementsInArray (types); ++i) if (isWindowsVersionOrLater (types[i])) return types[i]; jassertfalse; // need to support whatever new version is running! return UnknownOS; } String SystemStats::getOperatingSystemName() { const char* name = "Unknown OS"; switch (getOperatingSystemType()) { case Windows10: name = "Windows 10"; break; case Windows8_1: name = "Windows 8.1"; break; case Windows8_0: name = "Windows 8.0"; break; case Windows7: name = "Windows 7"; break; case WinVista: name = "Windows Vista"; break; case WinXP: name = "Windows XP"; break; case Win2000: name = "Windows 2000"; break; default: jassertfalse; break; // !! new type of OS? } return name; } String SystemStats::getDeviceDescription() { return String(); } bool SystemStats::isOperatingSystem64Bit() { #if JUCE_64BIT return true; #else typedef BOOL (WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandleA ("kernel32"), "IsWow64Process"); BOOL isWow64 = FALSE; return fnIsWow64Process != nullptr && fnIsWow64Process (GetCurrentProcess(), &isWow64) && isWow64 != FALSE; #endif } //============================================================================== int SystemStats::getMemorySizeInMegabytes() { MEMORYSTATUSEX mem; mem.dwLength = sizeof (mem); GlobalMemoryStatusEx (&mem); return (int) (mem.ullTotalPhys / (1024 * 1024)) + 1; } //============================================================================== String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue) { DWORD len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0); if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) return String (defaultValue); HeapBlock buffer (len); len = GetEnvironmentVariableW (name.toWideCharPointer(), buffer, len); return String (CharPointer_wchar_t (buffer), CharPointer_wchar_t (buffer + len)); } //============================================================================== uint32 juce_millisecondsSinceStartup() noexcept { return (uint32) timeGetTime(); } //============================================================================== class HiResCounterHandler { public: HiResCounterHandler() : hiResTicksOffset (0) { // This macro allows you to override the default timer-period // used on Windows. By default this is set to 1, because that has // always been the value used in JUCE apps, and changing it could // affect the behaviour of existing code, but you may wish to make // it larger (or set it to 0 to use the system default) to make your // app less demanding on the CPU. // For more info, see win32 documentation about the timeBeginPeriod // function. #ifndef JUCE_WIN32_TIMER_PERIOD #define JUCE_WIN32_TIMER_PERIOD 1 #endif #if JUCE_WIN32_TIMER_PERIOD > 0 const MMRESULT res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD); (void) res; jassert (res == TIMERR_NOERROR); #endif LARGE_INTEGER f; QueryPerformanceFrequency (&f); hiResTicksPerSecond = f.QuadPart; hiResTicksScaleFactor = 1000.0 / hiResTicksPerSecond; } inline int64 getHighResolutionTicks() noexcept { LARGE_INTEGER ticks; QueryPerformanceCounter (&ticks); const int64 mainCounterAsHiResTicks = (juce_millisecondsSinceStartup() * hiResTicksPerSecond) / 1000; const int64 newOffset = mainCounterAsHiResTicks - ticks.QuadPart; // fix for a very obscure PCI hardware bug that can make the counter // sometimes jump forwards by a few seconds.. const int64 offsetDrift = abs64 (newOffset - hiResTicksOffset); if (offsetDrift > (hiResTicksPerSecond >> 1)) hiResTicksOffset = newOffset; return ticks.QuadPart + hiResTicksOffset; } inline double getMillisecondCounterHiRes() noexcept { return getHighResolutionTicks() * hiResTicksScaleFactor; } int64 hiResTicksPerSecond, hiResTicksOffset; double hiResTicksScaleFactor; }; static HiResCounterHandler hiResCounterHandler; int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.hiResTicksPerSecond; } int64 Time::getHighResolutionTicks() noexcept { return hiResCounterHandler.getHighResolutionTicks(); } double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); } //============================================================================== static int64 juce_getClockCycleCounter() noexcept { #if JUCE_USE_MSVC_INTRINSICS // MS intrinsics version... return (int64) __rdtsc(); #elif JUCE_GCC // GNU inline asm version... unsigned int hi = 0, lo = 0; __asm__ __volatile__ ( "xor %%eax, %%eax \n\ xor %%edx, %%edx \n\ rdtsc \n\ movl %%eax, %[lo] \n\ movl %%edx, %[hi]" : : [hi] "m" (hi), [lo] "m" (lo) : "cc", "eax", "ebx", "ecx", "edx", "memory"); return (int64) ((((uint64) hi) << 32) | lo); #else // MSVC inline asm version... unsigned int hi = 0, lo = 0; __asm { xor eax, eax xor edx, edx rdtsc mov lo, eax mov hi, edx } return (int64) ((((uint64) hi) << 32) | lo); #endif } int SystemStats::getCpuSpeedInMegaherz() { const int64 cycles = juce_getClockCycleCounter(); const uint32 millis = Time::getMillisecondCounter(); int lastResult = 0; for (;;) { int n = 1000000; while (--n > 0) {} const uint32 millisElapsed = Time::getMillisecondCounter() - millis; const int64 cyclesNow = juce_getClockCycleCounter(); if (millisElapsed > 80) { const int newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000); if (millisElapsed > 500 || (lastResult == newResult && newResult > 100)) return newResult; lastResult = newResult; } } } //============================================================================== bool Time::setSystemTimeToThisTime() const { SYSTEMTIME st; st.wDayOfWeek = 0; st.wYear = (WORD) getYear(); st.wMonth = (WORD) (getMonth() + 1); st.wDay = (WORD) getDayOfMonth(); st.wHour = (WORD) getHours(); st.wMinute = (WORD) getMinutes(); st.wSecond = (WORD) getSeconds(); st.wMilliseconds = (WORD) (millisSinceEpoch % 1000); // do this twice because of daylight saving conversion problems - the // first one sets it up, the second one kicks it in. return SetLocalTime (&st) != 0 && SetLocalTime (&st) != 0; } int SystemStats::getPageSize() { SYSTEM_INFO systemInfo; GetNativeSystemInfo (&systemInfo); return (int) systemInfo.dwPageSize; } //============================================================================== String SystemStats::getLogonName() { TCHAR text [256] = { 0 }; DWORD len = (DWORD) numElementsInArray (text) - 1; GetUserName (text, &len); return String (text, len); } String SystemStats::getFullUserName() { return getLogonName(); } String SystemStats::getComputerName() { TCHAR text [MAX_COMPUTERNAME_LENGTH + 1] = { 0 }; DWORD len = (DWORD) numElementsInArray (text) - 1; GetComputerName (text, &len); return String (text, len); } static String getLocaleValue (LCID locale, LCTYPE key, const char* defaultValue) { TCHAR buffer [256] = { 0 }; if (GetLocaleInfo (locale, key, buffer, 255) > 0) return buffer; return defaultValue; } String SystemStats::getUserLanguage() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, "en"); } String SystemStats::getUserRegion() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, "US"); } String SystemStats::getDisplayLanguage() { DynamicLibrary dll ("kernel32.dll"); JUCE_LOAD_WINAPI_FUNCTION (dll, GetUserDefaultUILanguage, getUserDefaultUILanguage, LANGID, (void)) if (getUserDefaultUILanguage == nullptr) return "en"; const DWORD langID = MAKELCID (getUserDefaultUILanguage(), SORT_DEFAULT); String mainLang (getLocaleValue (langID, LOCALE_SISO639LANGNAME, "en")); String region (getLocaleValue (langID, LOCALE_SISO3166CTRYNAME, nullptr)); if (region.isNotEmpty()) mainLang << '-' << region; return mainLang; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp000066400000000000000000000444431320201440200311670ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ HWND juce_messageWindowHandle = 0; // (this is used by other parts of the codebase) void* getUser32Function (const char* functionName) { HMODULE module = GetModuleHandleA ("user32.dll"); jassert (module != 0); return (void*) GetProcAddress (module, functionName); } //============================================================================== #if ! JUCE_USE_MSVC_INTRINSICS // In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in // older ones we have to actually call the ops as win32 functions.. long juce_InterlockedExchange (volatile long* a, long b) noexcept { return InterlockedExchange (a, b); } long juce_InterlockedIncrement (volatile long* a) noexcept { return InterlockedIncrement (a); } long juce_InterlockedDecrement (volatile long* a) noexcept { return InterlockedDecrement (a); } long juce_InterlockedExchangeAdd (volatile long* a, long b) noexcept { return InterlockedExchangeAdd (a, b); } long juce_InterlockedCompareExchange (volatile long* a, long b, long c) noexcept { return InterlockedCompareExchange (a, b, c); } __int64 juce_InterlockedCompareExchange64 (volatile __int64* value, __int64 newValue, __int64 valueToCompare) noexcept { jassertfalse; // This operation isn't available in old MS compiler versions! __int64 oldValue = *value; if (oldValue == valueToCompare) *value = newValue; return oldValue; } #endif //============================================================================== CriticalSection::CriticalSection() noexcept { // (just to check the MS haven't changed this structure and broken things...) #if JUCE_VC7_OR_EARLIER static_jassert (sizeof (CRITICAL_SECTION) <= 24); #else static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (lock)); #endif InitializeCriticalSection ((CRITICAL_SECTION*) lock); } CriticalSection::~CriticalSection() noexcept { DeleteCriticalSection ((CRITICAL_SECTION*) lock); } void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_SECTION*) lock); } bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) lock) != FALSE; } void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) lock); } //============================================================================== WaitableEvent::WaitableEvent (const bool manualReset) noexcept : handle (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) {} WaitableEvent::~WaitableEvent() noexcept { CloseHandle (handle); } void WaitableEvent::signal() const noexcept { SetEvent (handle); } void WaitableEvent::reset() const noexcept { ResetEvent (handle); } bool WaitableEvent::wait (const int timeOutMs) const noexcept { return WaitForSingleObject (handle, (DWORD) timeOutMs) == WAIT_OBJECT_0; } //============================================================================== void JUCE_API juce_threadEntryPoint (void*); static unsigned int __stdcall threadEntryProc (void* userData) { if (juce_messageWindowHandle != 0) AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, 0), GetCurrentThreadId(), TRUE); juce_threadEntryPoint (userData); _endthreadex (0); return 0; } void Thread::launchThread() { unsigned int newThreadId; threadHandle = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId); threadId = (ThreadID) newThreadId; } void Thread::closeThreadHandle() { CloseHandle ((HANDLE) threadHandle); threadId = 0; threadHandle = 0; } void Thread::killThread() { if (threadHandle != 0) { #if JUCE_DEBUG OutputDebugStringA ("** Warning - Forced thread termination **\n"); #endif TerminateThread (threadHandle, 0); } } void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) { #if JUCE_DEBUG && JUCE_MSVC struct { DWORD dwType; LPCSTR szName; DWORD dwThreadID; DWORD dwFlags; } info; info.dwType = 0x1000; info.szName = name.toUTF8(); info.dwThreadID = GetCurrentThreadId(); info.dwFlags = 0; __try { RaiseException (0x406d1388 /*MS_VC_EXCEPTION*/, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info); } __except (EXCEPTION_CONTINUE_EXECUTION) {} #else (void) name; #endif } Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId() { return (ThreadID) (pointer_sized_int) GetCurrentThreadId(); } bool Thread::setThreadPriority (void* handle, int priority) { int pri = THREAD_PRIORITY_TIME_CRITICAL; if (priority < 1) pri = THREAD_PRIORITY_IDLE; else if (priority < 2) pri = THREAD_PRIORITY_LOWEST; else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL; else if (priority < 7) pri = THREAD_PRIORITY_NORMAL; else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL; else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST; if (handle == 0) handle = GetCurrentThread(); return SetThreadPriority (handle, pri) != FALSE; } void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) { SetThreadAffinityMask (GetCurrentThread(), affinityMask); } //============================================================================== struct SleepEvent { SleepEvent() noexcept : handle (CreateEvent (nullptr, FALSE, FALSE, #if JUCE_DEBUG _T("JUCE Sleep Event"))) #else nullptr)) #endif {} ~SleepEvent() noexcept { CloseHandle (handle); handle = 0; } HANDLE handle; }; static SleepEvent sleepEvent; void JUCE_CALLTYPE Thread::sleep (const int millisecs) { jassert (millisecs >= 0); if (millisecs >= 10 || sleepEvent.handle == 0) Sleep ((DWORD) millisecs); else // unlike Sleep() this is guaranteed to return to the current thread after // the time expires, so we'll use this for short waits, which are more likely // to need to be accurate WaitForSingleObject (sleepEvent.handle, (DWORD) millisecs); } void Thread::yield() { Sleep (0); } //============================================================================== static int lastProcessPriority = -1; // called when the app gains focus because Windows does weird things to process priority // when you swap apps, and this forces an update when the app is brought to the front. void juce_repeatLastProcessPriority() { if (lastProcessPriority >= 0) // (avoid changing this if it's not been explicitly set by the app..) { DWORD p; switch (lastProcessPriority) { case Process::LowPriority: p = IDLE_PRIORITY_CLASS; break; case Process::NormalPriority: p = NORMAL_PRIORITY_CLASS; break; case Process::HighPriority: p = HIGH_PRIORITY_CLASS; break; case Process::RealtimePriority: p = REALTIME_PRIORITY_CLASS; break; default: jassertfalse; return; // bad priority value } SetPriorityClass (GetCurrentProcess(), p); } } void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) { if (lastProcessPriority != (int) prior) { lastProcessPriority = (int) prior; juce_repeatLastProcessPriority(); } } JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() { return IsDebuggerPresent() != FALSE; } bool JUCE_CALLTYPE Process::isRunningUnderDebugger() { return juce_isRunningUnderDebugger(); } static void* currentModuleHandle = nullptr; void* JUCE_CALLTYPE Process::getCurrentModuleInstanceHandle() noexcept { if (currentModuleHandle == nullptr) currentModuleHandle = GetModuleHandleA (nullptr); return currentModuleHandle; } void JUCE_CALLTYPE Process::setCurrentModuleInstanceHandle (void* const newHandle) noexcept { currentModuleHandle = newHandle; } void JUCE_CALLTYPE Process::raisePrivilege() { jassertfalse; // xxx not implemented } void JUCE_CALLTYPE Process::lowerPrivilege() { jassertfalse; // xxx not implemented } void JUCE_CALLTYPE Process::terminate() { #if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS _CrtDumpMemoryLeaks(); #endif // bullet in the head in case there's a problem shutting down.. ExitProcess (1); } bool juce_isRunningInWine() { HMODULE ntdll = GetModuleHandleA ("ntdll"); return ntdll != 0 && GetProcAddress (ntdll, "wine_get_version") != nullptr; } //============================================================================== bool DynamicLibrary::open (const String& name) { close(); JUCE_TRY { handle = LoadLibrary (name.toWideCharPointer()); } JUCE_CATCH_ALL return handle != nullptr; } void DynamicLibrary::close() { JUCE_TRY { if (handle != nullptr) { FreeLibrary ((HMODULE) handle); handle = nullptr; } } JUCE_CATCH_ALL } void* DynamicLibrary::getFunction (const String& functionName) noexcept { return handle != nullptr ? (void*) GetProcAddress ((HMODULE) handle, functionName.toUTF8()) // (void* cast is required for mingw) : nullptr; } //============================================================================== class InterProcessLock::Pimpl { public: Pimpl (String name, const int timeOutMillisecs) : handle (0), refCount (1) { name = name.replaceCharacter ('\\', '/'); handle = CreateMutexW (0, TRUE, ("Global\\" + name).toWideCharPointer()); // Not 100% sure why a global mutex sometimes can't be allocated, but if it fails, fall back to // a local one. (A local one also sometimes fails on other machines so neither type appears to be // universally reliable) if (handle == 0) handle = CreateMutexW (0, TRUE, ("Local\\" + name).toWideCharPointer()); if (handle != 0 && GetLastError() == ERROR_ALREADY_EXISTS) { if (timeOutMillisecs == 0) { close(); return; } switch (WaitForSingleObject (handle, timeOutMillisecs < 0 ? INFINITE : timeOutMillisecs)) { case WAIT_OBJECT_0: case WAIT_ABANDONED: break; case WAIT_TIMEOUT: default: close(); break; } } } ~Pimpl() { close(); } void close() { if (handle != 0) { ReleaseMutex (handle); CloseHandle (handle); handle = 0; } } HANDLE handle; int refCount; }; InterProcessLock::InterProcessLock (const String& name_) : name (name_) { } InterProcessLock::~InterProcessLock() { } bool InterProcessLock::enter (const int timeOutMillisecs) { const ScopedLock sl (lock); if (pimpl == nullptr) { pimpl = new Pimpl (name, timeOutMillisecs); if (pimpl->handle == 0) pimpl = nullptr; } else { pimpl->refCount++; } return pimpl != nullptr; } void InterProcessLock::exit() { const ScopedLock sl (lock); // Trying to release the lock too many times! jassert (pimpl != nullptr); if (pimpl != nullptr && --(pimpl->refCount) == 0) pimpl = nullptr; } //============================================================================== class ChildProcess::ActiveProcess { public: ActiveProcess (const String& command, int streamFlags) : ok (false), readPipe (0), writePipe (0) { SECURITY_ATTRIBUTES securityAtts = { 0 }; securityAtts.nLength = sizeof (securityAtts); securityAtts.bInheritHandle = TRUE; if (CreatePipe (&readPipe, &writePipe, &securityAtts, 0) && SetHandleInformation (readPipe, HANDLE_FLAG_INHERIT, 0)) { STARTUPINFOW startupInfo = { 0 }; startupInfo.cb = sizeof (startupInfo); startupInfo.hStdOutput = (streamFlags | wantStdOut) != 0 ? writePipe : 0; startupInfo.hStdError = (streamFlags | wantStdErr) != 0 ? writePipe : 0; startupInfo.dwFlags = STARTF_USESTDHANDLES; ok = CreateProcess (nullptr, const_cast (command.toWideCharPointer()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, &startupInfo, &processInfo) != FALSE; } } ~ActiveProcess() { if (ok) { CloseHandle (processInfo.hThread); CloseHandle (processInfo.hProcess); } if (readPipe != 0) CloseHandle (readPipe); if (writePipe != 0) CloseHandle (writePipe); } bool isRunning() const noexcept { return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0; } int read (void* dest, int numNeeded) const noexcept { int total = 0; while (ok && numNeeded > 0) { DWORD available = 0; if (! PeekNamedPipe ((HANDLE) readPipe, nullptr, 0, nullptr, &available, nullptr)) break; const int numToDo = jmin ((int) available, numNeeded); if (available == 0) { if (! isRunning()) break; Thread::yield(); } else { DWORD numRead = 0; if (! ReadFile ((HANDLE) readPipe, dest, numToDo, &numRead, nullptr)) break; total += numRead; dest = addBytesToPointer (dest, numRead); numNeeded -= numRead; } } return total; } bool killProcess() const noexcept { return TerminateProcess (processInfo.hProcess, 0) != FALSE; } uint32 getExitCode() const noexcept { DWORD exitCode = 0; GetExitCodeProcess (processInfo.hProcess, &exitCode); return (uint32) exitCode; } bool ok; private: HANDLE readPipe, writePipe; PROCESS_INFORMATION processInfo; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) }; bool ChildProcess::start (const String& command, int streamFlags) { activeProcess = new ActiveProcess (command, streamFlags); if (! activeProcess->ok) activeProcess = nullptr; return activeProcess != nullptr; } bool ChildProcess::start (const StringArray& args, int streamFlags) { String escaped; for (int i = 0; i < args.size(); ++i) { String arg (args[i]); // If there are spaces, surround it with quotes. If there are quotes, // replace them with \" so that CommandLineToArgv will correctly parse them. if (arg.containsAnyOf ("\" ")) arg = arg.replace ("\"", "\\\"").quoted(); escaped << arg << ' '; } return start (escaped.trim(), streamFlags); } //============================================================================== struct HighResolutionTimer::Pimpl { Pimpl (HighResolutionTimer& t) noexcept : owner (t), periodMs (0) { } ~Pimpl() { jassert (periodMs == 0); } void start (int newPeriod) { if (newPeriod != periodMs) { stop(); periodMs = newPeriod; TIMECAPS tc; if (timeGetDevCaps (&tc, sizeof (tc)) == TIMERR_NOERROR) { const int actualPeriod = jlimit ((int) tc.wPeriodMin, (int) tc.wPeriodMax, newPeriod); timerID = timeSetEvent (actualPeriod, tc.wPeriodMin, callbackFunction, (DWORD_PTR) this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION | 0x100 /*TIME_KILL_SYNCHRONOUS*/); } } } void stop() { periodMs = 0; timeKillEvent (timerID); } HighResolutionTimer& owner; int periodMs; private: unsigned int timerID; static void __stdcall callbackFunction (UINT, UINT, DWORD_PTR userInfo, DWORD_PTR, DWORD_PTR) { if (Pimpl* const timer = reinterpret_cast (userInfo)) if (timer->periodMs != 0) timer->owner.hiResTimerCallback(); } JUCE_DECLARE_NON_COPYABLE (Pimpl) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/000077500000000000000000000000001320201440200252335ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp000066400000000000000000000112111320201440200305570ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ IPAddress::IPAddress() noexcept { address[0] = 0; address[1] = 0; address[2] = 0; address[3] = 0; } IPAddress::IPAddress (const uint8 bytes[4]) noexcept { address[0] = bytes[0]; address[1] = bytes[1]; address[2] = bytes[2]; address[3] = bytes[3]; } IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept { address[0] = a0; address[1] = a1; address[2] = a2; address[3] = a3; } IPAddress::IPAddress (uint32 n) noexcept { address[0] = (n >> 24); address[1] = (n >> 16) & 255; address[2] = (n >> 8) & 255; address[3] = (n & 255); } IPAddress::IPAddress (const String& adr) { StringArray tokens; tokens.addTokens (adr, ".", String()); for (int i = 0; i < 4; ++i) address[i] = (uint8) tokens[i].getIntValue(); } String IPAddress::toString() const { String s ((int) address[0]); for (int i = 1; i < 4; ++i) s << '.' << (int) address[i]; return s; } IPAddress IPAddress::any() noexcept { return IPAddress(); } IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); } IPAddress IPAddress::local() noexcept { return IPAddress (127, 0, 0, 1); } bool IPAddress::operator== (const IPAddress& other) const noexcept { return address[0] == other.address[0] && address[1] == other.address[1] && address[2] == other.address[2] && address[3] == other.address[3]; } bool IPAddress::operator!= (const IPAddress& other) const noexcept { return ! operator== (other); } #if ! JUCE_WINDOWS static void addAddress (const sockaddr_in* addr_in, Array& result) { in_addr_t addr = addr_in->sin_addr.s_addr; if (addr != INADDR_NONE) result.addIfNotAlreadyThere (IPAddress (ntohl (addr))); } static void findIPAddresses (int sock, Array& result) { ifconf cfg; HeapBlock buffer; int bufferSize = 1024; do { bufferSize *= 2; buffer.calloc ((size_t) bufferSize); cfg.ifc_len = bufferSize; cfg.ifc_buf = buffer; if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL) return; } while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6))); #if JUCE_MAC || JUCE_IOS while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in))) { if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result); cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; } #else for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i) { const ifreq& item = cfg.ifc_req[i]; if (item.ifr_addr.sa_family == AF_INET) addAddress ((const sockaddr_in*) &item.ifr_addr, result); } #endif } void IPAddress::findAllAddresses (Array& result) { const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control if (sock >= 0) { findIPAddresses (sock, result); ::close (sock); } } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h000066400000000000000000000062611320201440200302350ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_IPADDRESS_H_INCLUDED #define JUCE_IPADDRESS_H_INCLUDED //============================================================================== /** An IPV4 address. */ class JUCE_API IPAddress { public: //============================================================================== /** Populates a list of all the IP addresses that this machine is using. */ static void findAllAddresses (Array& results); //============================================================================== /** Creates a null address (0.0.0.0). */ IPAddress() noexcept; /** Creates an address from 4 bytes. */ explicit IPAddress (const uint8 bytes[4]) noexcept; /** Creates an address from 4 bytes. */ IPAddress (uint8 address1, uint8 address2, uint8 address3, uint8 address4) noexcept; /** Creates an address from a packed 32-bit integer, where the MSB is the first number in the address, and the LSB is the last. */ explicit IPAddress (uint32 asNativeEndian32Bit) noexcept; /** Parses a string IP address of the form "a.b.c.d". */ explicit IPAddress (const String& address); /** Returns a dot-separated string in the form "1.2.3.4" */ String toString() const; /** Returns an address meaning "any" (0.0.0.0) */ static IPAddress any() noexcept; /** Returns an address meaning "broadcast" (255.255.255.255) */ static IPAddress broadcast() noexcept; /** Returns an address meaning "localhost" (127.0.0.1) */ static IPAddress local() noexcept; bool operator== (const IPAddress& other) const noexcept; bool operator!= (const IPAddress& other) const noexcept; /** The elements of the IP address. */ uint8 address[4]; }; #endif // JUCE_IPADDRESS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp000066400000000000000000000051361320201440200306600ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ MACAddress::MACAddress() { zeromem (address, sizeof (address)); } MACAddress::MACAddress (const MACAddress& other) { memcpy (address, other.address, sizeof (address)); } MACAddress& MACAddress::operator= (const MACAddress& other) { memcpy (address, other.address, sizeof (address)); return *this; } MACAddress::MACAddress (const uint8 bytes[6]) { memcpy (address, bytes, sizeof (address)); } String MACAddress::toString() const { String s; for (size_t i = 0; i < sizeof (address); ++i) { s << String::toHexString ((int) address[i]).paddedLeft ('0', 2); if (i < sizeof (address) - 1) s << '-'; } return s; } int64 MACAddress::toInt64() const noexcept { int64 n = 0; for (int i = (int) sizeof (address); --i >= 0;) n = (n << 8) | address[i]; return n; } bool MACAddress::isNull() const noexcept { return toInt64() == 0; } bool MACAddress::operator== (const MACAddress& other) const noexcept { return memcmp (address, other.address, sizeof (address)) == 0; } bool MACAddress::operator!= (const MACAddress& other) const noexcept { return ! operator== (other); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h000066400000000000000000000062761320201440200303330ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_MACADDRESS_H_INCLUDED #define JUCE_MACADDRESS_H_INCLUDED //============================================================================== /** Represents a MAC network card adapter address ID. */ class JUCE_API MACAddress { public: //============================================================================== /** Populates a list of the MAC addresses of all the available network cards. */ static void findAllAddresses (Array& results); //============================================================================== /** Creates a null address (00-00-00-00-00-00). */ MACAddress(); /** Creates a copy of another address. */ MACAddress (const MACAddress&); /** Creates a copy of another address. */ MACAddress& operator= (const MACAddress&); /** Creates an address from 6 bytes. */ explicit MACAddress (const uint8 bytes[6]); /** Returns a pointer to the 6 bytes that make up this address. */ const uint8* getBytes() const noexcept { return address; } /** Returns a dash-separated string in the form "11-22-33-44-55-66" */ String toString() const; /** Returns the address in the lower 6 bytes of an int64. This uses a little-endian arrangement, with the first byte of the address being stored in the least-significant byte of the result value. */ int64 toInt64() const noexcept; /** Returns true if this address is null (00-00-00-00-00-00). */ bool isNull() const noexcept; bool operator== (const MACAddress&) const noexcept; bool operator!= (const MACAddress&) const noexcept; //============================================================================== private: uint8 address[6]; }; #endif // JUCE_MACADDRESS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp000066400000000000000000000040461320201440200306130ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ NamedPipe::NamedPipe() { } NamedPipe::~NamedPipe() { close(); } bool NamedPipe::openExisting (const String& pipeName) { close(); ScopedWriteLock sl (lock); currentPipeName = pipeName; return openInternal (pipeName, false); } bool NamedPipe::isOpen() const { return pimpl != nullptr; } bool NamedPipe::createNewPipe (const String& pipeName) { close(); ScopedWriteLock sl (lock); currentPipeName = pipeName; return openInternal (pipeName, true); } String NamedPipe::getName() const { return currentPipeName; } // other methods for this class are implemented in the platform-specific files libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h000066400000000000000000000074611320201440200302640ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_NAMEDPIPE_H_INCLUDED #define JUCE_NAMEDPIPE_H_INCLUDED //============================================================================== /** A cross-process pipe that can have data written to and read from it. Two processes can use NamedPipe objects to exchange blocks of data. @see InterprocessConnection */ class JUCE_API NamedPipe { public: //============================================================================== /** Creates a NamedPipe. */ NamedPipe(); /** Destructor. */ ~NamedPipe(); //============================================================================== /** Tries to open a pipe that already exists. Returns true if it succeeds. */ bool openExisting (const String& pipeName); /** Tries to create a new pipe. Returns true if it succeeds. */ bool createNewPipe (const String& pipeName); /** Closes the pipe, if it's open. */ void close(); /** True if the pipe is currently open. */ bool isOpen() const; /** Returns the last name that was used to try to open this pipe. */ String getName() const; //============================================================================== /** Reads data from the pipe. This will block until another thread has written enough data into the pipe to fill the number of bytes specified, or until another thread calls the cancelPendingReads() method. If the operation fails, it returns -1, otherwise, it will return the number of bytes read. If timeOutMilliseconds is less than zero, it will wait indefinitely, otherwise this is a maximum timeout for reading from the pipe. */ int read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds); /** Writes some data to the pipe. @returns the number of bytes written, or -1 on failure. */ int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds); private: //============================================================================== JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl) ScopedPointer pimpl; String currentPipeName; ReadWriteLock lock; bool openInternal (const String& pipeName, const bool createPipe); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NamedPipe) }; #endif // JUCE_NAMEDPIPE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp000066400000000000000000000522441320201440200302040ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #if JUCE_MSVC #pragma warning (push) #pragma warning (disable : 4127 4389 4018) #endif #ifndef AI_NUMERICSERV // (missing in older Mac SDKs) #define AI_NUMERICSERV 0x1000 #endif #if JUCE_WINDOWS typedef int juce_socklen_t; typedef SOCKET SocketHandle; #else typedef socklen_t juce_socklen_t; typedef int SocketHandle; #endif //============================================================================== namespace SocketHelpers { static void initSockets() { #if JUCE_WINDOWS static bool socketsStarted = false; if (! socketsStarted) { socketsStarted = true; WSADATA wsaData; const WORD wVersionRequested = MAKEWORD (1, 1); WSAStartup (wVersionRequested, &wsaData); } #endif } static bool resetSocketOptions (const SocketHandle handle, const bool isDatagram, const bool allowBroadcast) noexcept { const int sndBufSize = 65536; const int rcvBufSize = 65536; const int one = 1; return handle > 0 && setsockopt (handle, SOL_SOCKET, SO_RCVBUF, (const char*) &rcvBufSize, sizeof (rcvBufSize)) == 0 && setsockopt (handle, SOL_SOCKET, SO_SNDBUF, (const char*) &sndBufSize, sizeof (sndBufSize)) == 0 && (isDatagram ? ((! allowBroadcast) || setsockopt (handle, SOL_SOCKET, SO_BROADCAST, (const char*) &one, sizeof (one)) == 0) : (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (one)) == 0)); } static void closeSocket (volatile int& handle, CriticalSection& readLock, const bool isListener, int portNumber, bool& connected) noexcept { const SocketHandle h = handle; handle = -1; #if JUCE_WINDOWS ignoreUnused (portNumber, isListener, readLock); if (h != SOCKET_ERROR || connected) closesocket (h); // make sure any read process finishes before we delete the socket CriticalSection::ScopedLockType lock(readLock); connected = false; #else if (connected) { connected = false; if (isListener) { // need to do this to interrupt the accept() function.. StreamingSocket temp; temp.connect (IPAddress::local().toString(), portNumber, 1000); } } if (h != -1) { // unblock any pending read requests ::shutdown (h, SHUT_RDWR); { // see man-page of recv on linux about a race condition where the // shutdown command is lost if the receiving thread does not have // a chance to process before close is called. On Mac OS X shutdown // does not unblock a select call, so using a lock here will dead-lock // both threads. #if JUCE_LINUX CriticalSection::ScopedLockType lock (readLock); ::close (h); #else ::close (h); CriticalSection::ScopedLockType lock (readLock); #endif } } #endif } static bool bindSocket (const SocketHandle handle, const int port, const String& address) noexcept { if (handle <= 0 || port < 0) return false; struct sockaddr_in servTmpAddr; zerostruct (servTmpAddr); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) servTmpAddr.sin_family = PF_INET; servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); servTmpAddr.sin_port = htons ((uint16) port); if (address.isNotEmpty()) servTmpAddr.sin_addr.s_addr = ::inet_addr (address.toUTF8()); return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0; } static int getBoundPort (const SocketHandle handle) noexcept { if (handle <= 0) return -1; struct sockaddr_in sin_addr; socklen_t len = sizeof (sin_addr); if (getsockname (handle, (struct sockaddr*) &sin_addr, &len) == 0) return ntohs (sin_addr.sin_port); return -1; } static int readSocket (const SocketHandle handle, void* const destBuffer, const int maxBytesToRead, bool volatile& connected, const bool blockUntilSpecifiedAmountHasArrived, CriticalSection& readLock, String* senderIP = nullptr, int* senderPort = nullptr) noexcept { int bytesRead = 0; while (bytesRead < maxBytesToRead) { long bytesThisTime = -1; char* const buffer = static_cast (destBuffer) + bytesRead; const juce_socklen_t numToRead = (juce_socklen_t) (maxBytesToRead - bytesRead); { // avoid race-condition CriticalSection::ScopedTryLockType lock (readLock); if (lock.isLocked()) { if (senderIP == nullptr || senderPort == nullptr) { bytesThisTime = ::recv (handle, buffer, numToRead, 0); } else { sockaddr_in client; socklen_t clientLen = sizeof (sockaddr); bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen); *senderIP = String::fromUTF8 (inet_ntoa (client.sin_addr), 16); *senderPort = ntohs (client.sin_port); } } } if (bytesThisTime <= 0 || ! connected) { if (bytesRead == 0) bytesRead = -1; break; } bytesRead += bytesThisTime; if (! blockUntilSpecifiedAmountHasArrived) break; } return (int) bytesRead; } static int waitForReadiness (const volatile int& handle, CriticalSection& readLock, const bool forReading, const int timeoutMsecs) noexcept { // avoid race-condition CriticalSection::ScopedTryLockType lock (readLock); if (! lock.isLocked()) return -1; int h = handle; struct timeval timeout; struct timeval* timeoutp; if (timeoutMsecs >= 0) { timeout.tv_sec = timeoutMsecs / 1000; timeout.tv_usec = (timeoutMsecs % 1000) * 1000; timeoutp = &timeout; } else { timeoutp = 0; } fd_set rset, wset; FD_ZERO (&rset); FD_SET (h, &rset); FD_ZERO (&wset); FD_SET (h, &wset); fd_set* const prset = forReading ? &rset : nullptr; fd_set* const pwset = forReading ? nullptr : &wset; #if JUCE_WINDOWS if (select ((int) h + 1, prset, pwset, 0, timeoutp) < 0) return -1; #else { int result; while ((result = select (h + 1, prset, pwset, 0, timeoutp)) < 0 && errno == EINTR) { } if (result < 0) return -1; } #endif // we are closing if (handle < 0) return -1; { int opt; juce_socklen_t len = sizeof (opt); if (getsockopt (h, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 || opt != 0) return -1; } return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0; } static bool setSocketBlockingState (const SocketHandle handle, const bool shouldBlock) noexcept { #if JUCE_WINDOWS u_long nonBlocking = shouldBlock ? 0 : (u_long) 1; return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0; #else int socketFlags = fcntl (handle, F_GETFL, 0); if (socketFlags == -1) return false; if (shouldBlock) socketFlags &= ~O_NONBLOCK; else socketFlags |= O_NONBLOCK; return fcntl (handle, F_SETFL, socketFlags) == 0; #endif } static addrinfo* getAddressInfo (const bool isDatagram, const String& hostName, int portNumber) { struct addrinfo hints; zerostruct (hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; struct addrinfo* info = nullptr; if (getaddrinfo (hostName.toUTF8(), String (portNumber).toUTF8(), &hints, &info) == 0 && info != nullptr) return info; return nullptr; } static bool connectSocket (int volatile& handle, CriticalSection& readLock, const String& hostName, const int portNumber, const int timeOutMillisecs) noexcept { struct addrinfo* info = getAddressInfo (false, hostName, portNumber); if (info == nullptr) return false; if (handle < 0) handle = (int) socket (info->ai_family, info->ai_socktype, 0); if (handle < 0) { freeaddrinfo (info); return false; } setSocketBlockingState (handle, false); const int result = ::connect (handle, info->ai_addr, (socklen_t) info->ai_addrlen); freeaddrinfo (info); bool retval = (result >= 0); if (result < 0) { #if JUCE_WINDOWS if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK) #else if (errno == EINPROGRESS) #endif { if (waitForReadiness (handle, readLock, false, timeOutMillisecs) == 1) retval = true; } } setSocketBlockingState (handle, true); if (retval) resetSocketOptions (handle, false, false); return retval; } static void makeReusable (int handle) noexcept { const int reuse = 1; setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse)); } static bool multicast (int handle, const String& multicastIPAddress, const String& interfaceIPAddress, bool join) noexcept { struct ip_mreq mreq; zerostruct (mreq); mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toUTF8()); mreq.imr_interface.s_addr = INADDR_ANY; if (interfaceIPAddress.isNotEmpty()) mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toUTF8()); int joinCmd = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; return setsockopt (handle, IPPROTO_IP, joinCmd, (const char*) &mreq, sizeof (mreq)) == 0; } } //============================================================================== StreamingSocket::StreamingSocket() : portNumber (0), handle (-1), connected (false), isListener (false) { SocketHelpers::initSockets(); } StreamingSocket::StreamingSocket (const String& host, int portNum, int h) : hostName (host), portNumber (portNum), handle (h), connected (true), isListener (false) { SocketHelpers::initSockets(); SocketHelpers::resetSocketOptions (h, false, false); } StreamingSocket::~StreamingSocket() { close(); } //============================================================================== int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, bool shouldBlock) { return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, shouldBlock, readLock) : -1; } int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite) { if (isListener || ! connected) return -1; return (int) ::send (handle, (const char*) sourceBuffer, (juce_socklen_t) numBytesToWrite, 0); } //============================================================================== int StreamingSocket::waitUntilReady (const bool readyForReading, const int timeoutMsecs) const { return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs) : -1; } //============================================================================== bool StreamingSocket::bindToPort (const int port) { return bindToPort (port, String()); } bool StreamingSocket::bindToPort (const int port, const String& addr) { return SocketHelpers::bindSocket (handle, port, addr); } int StreamingSocket::getBoundPort() const noexcept { return SocketHelpers::getBoundPort (handle); } bool StreamingSocket::connect (const String& remoteHostName, const int remotePortNumber, const int timeOutMillisecs) { if (isListener) { jassertfalse; // a listener socket can't connect to another one! return false; } if (connected) close(); hostName = remoteHostName; portNumber = remotePortNumber; isListener = false; connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName, remotePortNumber, timeOutMillisecs); if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false))) { close(); return false; } return true; } void StreamingSocket::close() { SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected); hostName.clear(); portNumber = 0; handle = -1; isListener = false; } //============================================================================== bool StreamingSocket::createListener (const int newPortNumber, const String& localHostName) { if (connected) close(); hostName = "listener"; portNumber = newPortNumber; isListener = true; struct sockaddr_in servTmpAddr; zerostruct (servTmpAddr); servTmpAddr.sin_family = PF_INET; servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); if (localHostName.isNotEmpty()) servTmpAddr.sin_addr.s_addr = ::inet_addr (localHostName.toUTF8()); servTmpAddr.sin_port = htons ((uint16) portNumber); handle = (int) socket (AF_INET, SOCK_STREAM, 0); if (handle < 0) return false; #if ! JUCE_WINDOWS // on windows, adding this option produces behaviour different to posix SocketHelpers::makeReusable (handle); #endif if (bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) < 0 || listen (handle, SOMAXCONN) < 0) { close(); return false; } connected = true; return true; } StreamingSocket* StreamingSocket::waitForNextConnection() const { // To call this method, you first have to use createListener() to // prepare this socket as a listener. jassert (isListener || ! connected); if (connected && isListener) { struct sockaddr_storage address; juce_socklen_t len = sizeof (address); const int newSocket = (int) accept (handle, (struct sockaddr*) &address, &len); if (newSocket >= 0 && connected) return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), portNumber, newSocket); } return nullptr; } bool StreamingSocket::isLocal() const noexcept { return hostName == "127.0.0.1"; } //============================================================================== //============================================================================== DatagramSocket::DatagramSocket (const bool canBroadcast) : handle (-1), isBound (false), lastServerPort (-1), lastServerAddress (nullptr) { SocketHelpers::initSockets(); handle = (int) socket (AF_INET, SOCK_DGRAM, 0); SocketHelpers::resetSocketOptions (handle, true, canBroadcast); SocketHelpers::makeReusable (handle); } DatagramSocket::~DatagramSocket() { if (lastServerAddress != nullptr) freeaddrinfo (static_cast (lastServerAddress)); bool connected = false; SocketHelpers::closeSocket (handle, readLock, false, 0, connected); } bool DatagramSocket::bindToPort (const int port) { return bindToPort (port, String()); } bool DatagramSocket::bindToPort (const int port, const String& addr) { if (SocketHelpers::bindSocket (handle, port, addr)) { isBound = true; lastBindAddress = addr; return true; } return false; } int DatagramSocket::getBoundPort() const noexcept { return isBound ? SocketHelpers::getBoundPort (handle) : -1; } //============================================================================== int DatagramSocket::waitUntilReady (const bool readyForReading, const int timeoutMsecs) const { return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs); } int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) { bool connected = true; return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, shouldBlock, readLock) : -1; } int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock, String& senderIPAddress, int& senderPort) { bool connected = true; return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, shouldBlock, readLock, &senderIPAddress, &senderPort) : -1; } int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, const void* sourceBuffer, int numBytesToWrite) { struct addrinfo*& info = reinterpret_cast (lastServerAddress); // getaddrinfo can be quite slow so cache the result of the address lookup if (info == nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort) { if (info != nullptr) freeaddrinfo (info); if ((info = SocketHelpers::getAddressInfo (true, remoteHostname, remotePortNumber)) == nullptr) return -1; lastServerHost = remoteHostname; lastServerPort = remotePortNumber; } return (int) ::sendto (handle, (const char*) sourceBuffer, (juce_socklen_t) numBytesToWrite, 0, info->ai_addr, (socklen_t) info->ai_addrlen); } bool DatagramSocket::joinMulticast (const String& multicastIPAddress) { if (! isBound) return false; return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, true); } bool DatagramSocket::leaveMulticast (const String& multicastIPAddress) { if (! isBound) return false; return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, false); } #if JUCE_MSVC #pragma warning (pop) #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_Socket.h000066400000000000000000000331311320201440200276430ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SOCKET_H_INCLUDED #define JUCE_SOCKET_H_INCLUDED //============================================================================== /** A wrapper for a streaming (TCP) socket. This allows low-level use of sockets; for an easier-to-use messaging layer on top of sockets, you could also try the InterprocessConnection class. @see DatagramSocket, InterprocessConnection, InterprocessConnectionServer */ class JUCE_API StreamingSocket { public: //============================================================================== /** Creates an uninitialised socket. To connect it, use the connect() method, after which you can read() or write() to it. To wait for other sockets to connect to this one, the createListener() method enters "listener" mode, and can be used to spawn new sockets for each connection that comes along. */ StreamingSocket(); /** Destructor. */ ~StreamingSocket(); //============================================================================== /** Binds the socket to the specified local port. @returns true on success; false may indicate that another socket is already bound on the same port */ bool bindToPort (int localPortNumber); /** Binds the socket to the specified local port and local address. If localAddress is not an empty string then the socket will be bound to localAddress as well. This is useful if you would like to bind your socket to a specific network adapter. Note that localAddress must be an IP address assigned to one of your network address otherwise this function will fail. @returns true on success; false may indicate that another socket is already bound on the same port @see bindToPort(int localPortNumber), IPAddress::findAllAddresses */ bool bindToPort (int localPortNumber, const String& localAddress); /** Returns the local port number to which this socket is currently bound. This is useful if you need to know to which port the OS has actually bound your socket when calling the constructor or bindToPort with zero as the localPortNumber argument. Returns -1 if the function fails. */ int getBoundPort() const noexcept; /** Tries to connect the socket to hostname:port. If timeOutMillisecs is 0, then this method will block until the operating system rejects the connection (which could take a long time). @returns true if it succeeds. @see isConnected */ bool connect (const String& remoteHostname, int remotePortNumber, int timeOutMillisecs = 3000); /** True if the socket is currently connected. */ bool isConnected() const noexcept { return connected; } /** Closes the connection. */ void close(); /** Returns the name of the currently connected host. */ const String& getHostName() const noexcept { return hostName; } /** Returns the port number that's currently open. */ int getPort() const noexcept { return portNumber; } /** True if the socket is connected to this machine rather than over the network. */ bool isLocal() const noexcept; /** Returns the OS's socket handle that's currently open. */ int getRawSocketHandle() const noexcept { return handle; } //============================================================================== /** Waits until the socket is ready for reading or writing. If readyForReading is true, it will wait until the socket is ready for reading; if false, it will wait until it's ready for writing. If the timeout is < 0, it will wait forever, or else will give up after the specified time. If the socket is ready on return, this returns 1. If it times-out before the socket becomes ready, it returns 0. If an error occurs, it returns -1. */ int waitUntilReady (bool readyForReading, int timeoutMsecs) const; /** Reads bytes from the socket. If blockUntilSpecifiedAmountHasArrived is true, the method will block until maxBytesToRead bytes have been read, (or until an error occurs). If this flag is false, the method will return as much data as is currently available without blocking. @returns the number of bytes read, or -1 if there was an error. @see waitUntilReady */ int read (void* destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived); /** Writes bytes to the socket from a buffer. Note that this method will block unless you have checked the socket is ready for writing before calling it (see the waitUntilReady() method). @returns the number of bytes written, or -1 if there was an error. */ int write (const void* sourceBuffer, int numBytesToWrite); //============================================================================== /** Puts this socket into "listener" mode. When in this mode, your thread can call waitForNextConnection() repeatedly, which will spawn new sockets for each new connection, so that these can be handled in parallel by other threads. @param portNumber the port number to listen on @param localHostName the interface address to listen on - pass an empty string to listen on all addresses @returns true if it manages to open the socket successfully. @see waitForNextConnection */ bool createListener (int portNumber, const String& localHostName = String()); /** When in "listener" mode, this waits for a connection and spawns it as a new socket. The object that gets returned will be owned by the caller. This method can only be called after using createListener(). @see createListener */ StreamingSocket* waitForNextConnection() const; private: //============================================================================== String hostName; int volatile portNumber, handle; bool connected, isListener; mutable CriticalSection readLock; StreamingSocket (const String& hostname, int portNumber, int handle); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StreamingSocket) }; //============================================================================== /** A wrapper for a datagram (UDP) socket. This allows low-level use of sockets; for an easier-to-use messaging layer on top of sockets, you could also try the InterprocessConnection class. @see StreamingSocket, InterprocessConnection, InterprocessConnectionServer */ class JUCE_API DatagramSocket { public: //============================================================================== /** Creates a datagram socket. You first need to bind this socket to a port with bindToPort if you intend to read from this socket. If enableBroadcasting is true, the socket will be allowed to send broadcast messages (may require extra privileges on linux) */ DatagramSocket (bool enableBroadcasting = false); /** Destructor. */ ~DatagramSocket(); //============================================================================== /** Binds the socket to the specified local port. The localPortNumber is the port on which to bind this socket. If this value is 0, the port number is assigned by the operating system. @returns true on success; false may indicate that another socket is already bound on the same port */ bool bindToPort (int localPortNumber); /** Binds the socket to the specified local port and local address. If localAddress is not an empty string then the socket will be bound to localAddress as well. This is useful if you would like to bind your socket to a specific network adapter. Note that localAddress must be an IP address assigned to one of your network address otherwise this function will fail. @returns true on success; false may indicate that another socket is already bound on the same port @see bindToPort(int localPortNumber), IPAddress::findAllAddresses */ bool bindToPort (int localPortNumber, const String& localAddress); /** Returns the local port number to which this socket is currently bound. This is useful if you need to know to which port the OS has actually bound your socket when bindToPort was called with zero. Returns -1 if the socket didn't bind to any port yet or an error occured. */ int getBoundPort() const noexcept; /** Returns the OS's socket handle that's currently open. */ int getRawSocketHandle() const noexcept { return handle; } //============================================================================== /** Waits until the socket is ready for reading or writing. If readyForReading is true, it will wait until the socket is ready for reading; if false, it will wait until it's ready for writing. If the timeout is < 0, it will wait forever, or else will give up after the specified time. If the socket is ready on return, this returns 1. If it times-out before the socket becomes ready, it returns 0. If an error occurs, it returns -1. */ int waitUntilReady (bool readyForReading, int timeoutMsecs) const; /** Reads bytes from the socket. If blockUntilSpecifiedAmountHasArrived is true, the method will block until maxBytesToRead bytes have been read, (or until an error occurs). If this flag is false, the method will return as much data as is currently available without blocking. @returns the number of bytes read, or -1 if there was an error. @see waitUntilReady */ int read (void* destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived); /** Reads bytes from the socket and return the IP address of the sender. If blockUntilSpecifiedAmountHasArrived is true, the method will block until maxBytesToRead bytes have been read, (or until an error occurs). If this flag is false, the method will return as much data as is currently available without blocking. @returns the number of bytes read, or -1 if there was an error. On a successful result, the senderIPAddress value will be set to the IP of the sender. @see waitUntilReady */ int read (void* destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived, String& senderIPAddress, int& senderPortNumber); /** Writes bytes to the socket from a buffer. Note that this method will block unless you have checked the socket is ready for writing before calling it (see the waitUntilReady() method). @returns the number of bytes written, or -1 if there was an error. */ int write (const String& remoteHostname, int remotePortNumber, const void* sourceBuffer, int numBytesToWrite); //============================================================================== /** Join a multicast group @returns true if it succeeds. */ bool joinMulticast (const String& multicastIPAddress); /** Leave a multicast group @returns true if it succeeds. */ bool leaveMulticast (const String& multicastIPAddress); private: //============================================================================== int handle; bool isBound; String lastBindAddress, lastServerHost; int lastServerPort; void* lastServerAddress; mutable CriticalSection readLock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DatagramSocket) }; #endif // JUCE_SOCKET_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp000066400000000000000000000377351320201440200274260ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ URL::URL() { } URL::URL (const String& u) : url (u) { int i = url.indexOfChar ('?'); if (i >= 0) { do { const int nextAmp = url.indexOfChar (i + 1, '&'); const int equalsPos = url.indexOfChar (i + 1, '='); if (equalsPos > i + 1) { if (nextAmp < 0) { addParameter (removeEscapeChars (url.substring (i + 1, equalsPos)), removeEscapeChars (url.substring (equalsPos + 1))); } else if (nextAmp > 0 && equalsPos < nextAmp) { addParameter (removeEscapeChars (url.substring (i + 1, equalsPos)), removeEscapeChars (url.substring (equalsPos + 1, nextAmp))); } } i = nextAmp; } while (i >= 0); url = url.upToFirstOccurrenceOf ("?", false, false); } } URL::URL (const String& u, int) : url (u) {} URL URL::createWithoutParsing (const String& u) { return URL (u, 0); } URL::URL (const URL& other) : url (other.url), postData (other.postData), parameterNames (other.parameterNames), parameterValues (other.parameterValues), filesToUpload (other.filesToUpload) { } URL& URL::operator= (const URL& other) { url = other.url; postData = other.postData; parameterNames = other.parameterNames; parameterValues = other.parameterValues; filesToUpload = other.filesToUpload; return *this; } bool URL::operator== (const URL& other) const { return url == other.url && postData == other.postData && parameterNames == other.parameterNames && parameterValues == other.parameterValues && filesToUpload == other.filesToUpload; } bool URL::operator!= (const URL& other) const { return ! operator== (other); } URL::~URL() { } namespace URLHelpers { static String getMangledParameters (const URL& url) { jassert (url.getParameterNames().size() == url.getParameterValues().size()); String p; for (int i = 0; i < url.getParameterNames().size(); ++i) { if (i > 0) p << '&'; p << URL::addEscapeChars (url.getParameterNames()[i], true) << '=' << URL::addEscapeChars (url.getParameterValues()[i], true); } return p; } static int findEndOfScheme (const String& url) { int i = 0; while (CharacterFunctions::isLetterOrDigit (url[i]) || url[i] == '+' || url[i] == '-' || url[i] == '.') ++i; return url[i] == ':' ? i + 1 : 0; } static int findStartOfNetLocation (const String& url) { int start = findEndOfScheme (url); while (url[start] == '/') ++start; return start; } static int findStartOfPath (const String& url) { return url.indexOfChar (findStartOfNetLocation (url), '/') + 1; } static void concatenatePaths (String& path, const String& suffix) { if (! path.endsWithChar ('/')) path << '/'; if (suffix.startsWithChar ('/')) path += suffix.substring (1); else path += suffix; } } void URL::addParameter (const String& name, const String& value) { parameterNames.add (name); parameterValues.add (value); } String URL::toString (const bool includeGetParameters) const { if (includeGetParameters && parameterNames.size() > 0) return url + "?" + URLHelpers::getMangledParameters (*this); return url; } bool URL::isEmpty() const noexcept { return url.isEmpty(); } bool URL::isWellFormed() const { //xxx TODO return url.isNotEmpty(); } String URL::getDomain() const { const int start = URLHelpers::findStartOfNetLocation (url); const int end1 = url.indexOfChar (start, '/'); const int end2 = url.indexOfChar (start, ':'); const int end = (end1 < 0 && end2 < 0) ? std::numeric_limits::max() : ((end1 < 0 || end2 < 0) ? jmax (end1, end2) : jmin (end1, end2)); return url.substring (start, end); } String URL::getSubPath() const { const int startOfPath = URLHelpers::findStartOfPath (url); return startOfPath <= 0 ? String() : url.substring (startOfPath); } String URL::getScheme() const { return url.substring (0, URLHelpers::findEndOfScheme (url) - 1); } int URL::getPort() const { const int colonPos = url.indexOfChar (URLHelpers::findStartOfNetLocation (url), ':'); return colonPos > 0 ? url.substring (colonPos + 1).getIntValue() : 0; } URL URL::withNewSubPath (const String& newPath) const { const int startOfPath = URLHelpers::findStartOfPath (url); URL u (*this); if (startOfPath > 0) u.url = url.substring (0, startOfPath); URLHelpers::concatenatePaths (u.url, newPath); return u; } URL URL::getChildURL (const String& subPath) const { URL u (*this); URLHelpers::concatenatePaths (u.url, subPath); return u; } void URL::createHeadersAndPostData (String& headers, MemoryBlock& headersAndPostData) const { MemoryOutputStream data (headersAndPostData, false); if (filesToUpload.size() > 0) { // (this doesn't currently support mixing custom post-data with uploads..) jassert (postData.isEmpty()); const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; data << "--" << boundary; for (int i = 0; i < parameterNames.size(); ++i) { data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i] << "\"\r\n\r\n" << parameterValues[i] << "\r\n--" << boundary; } for (int i = 0; i < filesToUpload.size(); ++i) { const Upload& f = *filesToUpload.getObjectPointerUnchecked(i); data << "\r\nContent-Disposition: form-data; name=\"" << f.parameterName << "\"; filename=\"" << f.filename << "\"\r\n"; if (f.mimeType.isNotEmpty()) data << "Content-Type: " << f.mimeType << "\r\n"; data << "Content-Transfer-Encoding: binary\r\n\r\n"; if (f.data != nullptr) data << *f.data; else data << f.file; data << "\r\n--" << boundary; } data << "--\r\n"; } else { data << URLHelpers::getMangledParameters (*this) << postData; // if the user-supplied headers didn't contain a content-type, add one now.. if (! headers.containsIgnoreCase ("Content-Type")) headers << "Content-Type: application/x-www-form-urlencoded\r\n"; headers << "Content-length: " << (int) data.getDataSize() << "\r\n"; } } //============================================================================== bool URL::isProbablyAWebsiteURL (const String& possibleURL) { static const char* validProtocols[] = { "http:", "ftp:", "https:" }; for (int i = 0; i < numElementsInArray (validProtocols); ++i) if (possibleURL.startsWithIgnoreCase (validProtocols[i])) return true; if (possibleURL.containsChar ('@') || possibleURL.containsChar (' ')) return false; const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false) .fromLastOccurrenceOf (".", false, false)); return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3; } bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) { const int atSign = possibleEmailAddress.indexOfChar ('@'); return atSign > 0 && possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1) && ! possibleEmailAddress.endsWithChar ('.'); } //============================================================================== InputStream* URL::createInputStream (const bool usePostCommand, OpenStreamProgressCallback* const progressCallback, void* const progressCallbackContext, String headers, const int timeOutMs, StringPairArray* const responseHeaders, int* statusCode, const int numRedirectsToFollow) const { MemoryBlock headersAndPostData; if (! headers.endsWithChar ('\n')) headers << "\r\n"; if (usePostCommand) createHeadersAndPostData (headers, headersAndPostData); if (! headers.endsWithChar ('\n')) headers << "\r\n"; ScopedPointer wi (new WebInputStream (toString (! usePostCommand), usePostCommand, headersAndPostData, progressCallback, progressCallbackContext, headers, timeOutMs, responseHeaders, numRedirectsToFollow)); if (statusCode != nullptr) *statusCode = wi->statusCode; return wi->isError() ? nullptr : wi.release(); } //============================================================================== bool URL::readEntireBinaryStream (MemoryBlock& destData, const bool usePostCommand) const { const ScopedPointer in (createInputStream (usePostCommand)); if (in != nullptr) { in->readIntoMemoryBlock (destData); return true; } return false; } String URL::readEntireTextStream (const bool usePostCommand) const { const ScopedPointer in (createInputStream (usePostCommand)); if (in != nullptr) return in->readEntireStreamAsString(); return String(); } XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const { return XmlDocument::parse (readEntireTextStream (usePostCommand)); } //============================================================================== URL URL::withParameter (const String& parameterName, const String& parameterValue) const { URL u (*this); u.addParameter (parameterName, parameterValue); return u; } URL URL::withParameters (const StringPairArray& parametersToAdd) const { URL u (*this); for (int i = 0; i < parametersToAdd.size(); ++i) u.addParameter (parametersToAdd.getAllKeys()[i], parametersToAdd.getAllValues()[i]); return u; } URL URL::withPOSTData (const String& newPostData) const { URL u (*this); u.postData = newPostData; return u; } URL::Upload::Upload (const String& param, const String& name, const String& mime, const File& f, MemoryBlock* mb) : parameterName (param), filename (name), mimeType (mime), file (f), data (mb) { jassert (mimeType.isNotEmpty()); // You need to supply a mime type! } URL URL::withUpload (Upload* const f) const { URL u (*this); for (int i = u.filesToUpload.size(); --i >= 0;) if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName) u.filesToUpload.remove (i); u.filesToUpload.add (f); return u; } URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload, const String& mimeType) const { return withUpload (new Upload (parameterName, fileToUpload.getFileName(), mimeType, fileToUpload, nullptr)); } URL URL::withDataToUpload (const String& parameterName, const String& filename, const MemoryBlock& fileContentToUpload, const String& mimeType) const { return withUpload (new Upload (parameterName, filename, mimeType, File(), new MemoryBlock (fileContentToUpload))); } //============================================================================== String URL::removeEscapeChars (const String& s) { String result (s.replaceCharacter ('+', ' ')); if (! result.containsChar ('%')) return result; // We need to operate on the string as raw UTF8 chars, and then recombine them into unicode // after all the replacements have been made, so that multi-byte chars are handled. Array utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8()); for (int i = 0; i < utf8.size(); ++i) { if (utf8.getUnchecked(i) == '%') { const int hexDigit1 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 1]); const int hexDigit2 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 2]); if (hexDigit1 >= 0 && hexDigit2 >= 0) { utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2)); utf8.removeRange (i + 1, 2); } } } return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size()); } String URL::addEscapeChars (const String& s, const bool isParameter) { const CharPointer_UTF8 legalChars (isParameter ? "_-.*!'()" : ",$_-.*!'()"); Array utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8()); for (int i = 0; i < utf8.size(); ++i) { const char c = utf8.getUnchecked(i); if (! (CharacterFunctions::isLetterOrDigit (c) || legalChars.indexOf ((juce_wchar) c) >= 0)) { utf8.set (i, '%'); utf8.insert (++i, "0123456789ABCDEF" [((uint8) c) >> 4]); utf8.insert (++i, "0123456789ABCDEF" [c & 15]); } } return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size()); } //============================================================================== bool URL::launchInDefaultBrowser() const { String u (toString (true)); if (u.containsChar ('@') && ! u.containsChar (':')) u = "mailto:" + u; return Process::openDocument (u, String()); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/network/juce_URL.h000066400000000000000000000424261320201440200270640ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_URL_H_INCLUDED #define JUCE_URL_H_INCLUDED //============================================================================== /** Represents a URL and has a bunch of useful functions to manipulate it. This class can be used to launch URLs in browsers, and also to create InputStreams that can read from remote http or ftp sources. */ class JUCE_API URL { public: //============================================================================== /** Creates an empty URL. */ URL(); /** Creates a URL from a string. This will parse any embedded parameters after a '?' character and store them in the list (see getParameterNames etc). If you don't want this to happen, you can use createWithoutParsing(). */ URL (const String& url); /** Creates a copy of another URL. */ URL (const URL& other); /** Destructor. */ ~URL(); /** Copies this URL from another one. */ URL& operator= (const URL& other); /** Compares two URLs. All aspects of the URLs must be identical for them to match, including any parameters, upload files, etc. */ bool operator== (const URL&) const; bool operator!= (const URL&) const; //============================================================================== /** Returns a string version of the URL. If includeGetParameters is true and any parameters have been set with the withParameter() method, then the string will have these appended on the end and url-encoded. */ String toString (bool includeGetParameters) const; /** Returns true if the URL is an empty string. */ bool isEmpty() const noexcept; /** True if it seems to be valid. */ bool isWellFormed() const; /** Returns just the domain part of the URL. E.g. for "http://www.xyz.com/foobar", this will return "www.xyz.com". */ String getDomain() const; /** Returns the path part of the URL. E.g. for "http://www.xyz.com/foo/bar?x=1", this will return "foo/bar". */ String getSubPath() const; /** Returns the scheme of the URL. E.g. for "http://www.xyz.com/foobar", this will return "http". (It won't include the colon). */ String getScheme() const; /** Attempts to read a port number from the URL. @returns the port number, or 0 if none is explicitly specified. */ int getPort() const; /** Returns a new version of this URL that uses a different sub-path. E.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with "bar", it'll return "http://www.xyz.com/bar?x=1". */ URL withNewSubPath (const String& newPath) const; /** Returns a new URL that refers to a sub-path relative to this one. E.g. if the URL is "http://www.xyz.com/foo" and you call this with "bar", it'll return "http://www.xyz.com/foo/bar". Note that there's no way for this method to know whether the original URL is a file or directory, so it's up to you to make sure it's a directory. It also won't attempt to be smart about the content of the childPath string, so if this string is an absolute URL, it'll still just get bolted onto the end of the path. @see File::getChildFile */ URL getChildURL (const String& subPath) const; //============================================================================== /** Returns a copy of this URL, with a GET or POST parameter added to the end. Any control characters in the value will be encoded. e.g. calling "withParameter ("amount", "some fish") for the url "www.fish.com" would produce a new url whose toString(true) method would return "www.fish.com?amount=some+fish". @see getParameterNames, getParameterValues */ URL withParameter (const String& parameterName, const String& parameterValue) const; /** Returns a copy of this URL, with a set of GET or POST parameters added. This is a convenience method, equivalent to calling withParameter for each value. @see withParameter */ URL withParameters (const StringPairArray& parametersToAdd) const; /** Returns a copy of this URL, with a file-upload type parameter added to it. When performing a POST where one of your parameters is a binary file, this lets you specify the file. Note that the filename is stored, but the file itself won't actually be read until this URL is later used to create a network input stream. If you want to upload data from memory, use withDataToUpload(). @see withDataToUpload */ URL withFileToUpload (const String& parameterName, const File& fileToUpload, const String& mimeType) const; /** Returns a copy of this URL, with a file-upload type parameter added to it. When performing a POST where one of your parameters is a binary file, this lets you specify the file content. Note that the filename parameter should not be a full path, it's just the last part of the filename. @see withFileToUpload */ URL withDataToUpload (const String& parameterName, const String& filename, const MemoryBlock& fileContentToUpload, const String& mimeType) const; /** Returns an array of the names of all the URL's parameters. E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would contain two items: "type" and "amount". You can call getParameterValues() to get the corresponding value of each parameter. Note that the list can contain multiple parameters with the same name. @see getParameterValues, withParameter */ const StringArray& getParameterNames() const noexcept { return parameterNames; } /** Returns an array of the values of all the URL's parameters. E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would contain two items: "haddock" and "some fish". The values returned will have been cleaned up to remove any escape characters. You can call getParameterNames() to get the corresponding name of each parameter. Note that the list can contain multiple parameters with the same name. @see getParameterNames, withParameter */ const StringArray& getParameterValues() const noexcept { return parameterValues; } /** Returns a copy of this URL, with a block of data to send as the POST data. If you're setting the POST data, be careful not to have any parameters set as well, otherwise it'll all get thrown in together, and might not have the desired effect. If the URL already contains some POST data, this will replace it, rather than being appended to it. This data will only be used if you specify a post operation when you call createInputStream(). */ URL withPOSTData (const String& postData) const; /** Returns the data that was set using withPOSTData(). */ const String& getPostData() const noexcept { return postData; } //============================================================================== /** Tries to launch the system's default browser to open the URL. Returns true if this seems to have worked. */ bool launchInDefaultBrowser() const; //============================================================================== /** Takes a guess as to whether a string might be a valid website address. This isn't foolproof! */ static bool isProbablyAWebsiteURL (const String& possibleURL); /** Takes a guess as to whether a string might be a valid email address. This isn't foolproof! */ static bool isProbablyAnEmailAddress (const String& possibleEmailAddress); //============================================================================== /** This callback function can be used by the createInputStream() method. It allows your app to receive progress updates during a lengthy POST operation. If you want to continue the operation, this should return true, or false to abort. */ typedef bool (OpenStreamProgressCallback) (void* context, int bytesSent, int totalBytes); /** Attempts to open a stream that can read from this URL. Note that on some platforms (Android, for example) it's not permitted to do any network action from the message thread, so you must only call it from a background thread. @param usePostCommand if true, it will try to do use a http 'POST' to pass the parameters, otherwise it'll encode them into the URL and do a 'GET'. @param progressCallback if this is non-zero, it lets you supply a callback function to keep track of the operation's progress. This can be useful for lengthy POST operations, so that you can provide user feedback. @param progressCallbackContext if a callback is specified, this value will be passed to the function @param extraHeaders 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. @param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If a negative number, it will be infinite. Otherwise it specifies a time in milliseconds. @param responseHeaders if this is non-null, all the (key, value) pairs received as headers in the response will be stored in this array @param statusCode if this is non-null, it will get set to the http status code, if one is known, or 0 if a code isn't available @param numRedirectsToFollow specifies the number of redirects that will be followed before returning a response (ignored for Android which follows up to 5 redirects) @returns an input stream that the caller must delete, or a null pointer if there was an error trying to open it. */ InputStream* createInputStream (bool usePostCommand, OpenStreamProgressCallback* progressCallback = nullptr, void* progressCallbackContext = nullptr, String extraHeaders = String(), int connectionTimeOutMs = 0, StringPairArray* responseHeaders = nullptr, int* statusCode = nullptr, int numRedirectsToFollow = 5) const; //============================================================================== /** Tries to download the entire contents of this URL into a binary data block. If it succeeds, this will return true and append the data it read onto the end of the memory block. Note that on some platforms (Android, for example) it's not permitted to do any network action from the message thread, so you must only call it from a background thread. @param destData the memory block to append the new data to @param usePostCommand whether to use a POST command to get the data (uses a GET command if this is false) @see readEntireTextStream, readEntireXmlStream */ bool readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand = false) const; /** Tries to download the entire contents of this URL as a string. If it fails, this will return an empty string, otherwise it will return the contents of the downloaded file. If you need to distinguish between a read operation that fails and one that returns an empty string, you'll need to use a different method, such as readEntireBinaryStream(). Note that on some platforms (Android, for example) it's not permitted to do any network action from the message thread, so you must only call it from a background thread. @param usePostCommand whether to use a POST command to get the data (uses a GET command if this is false) @see readEntireBinaryStream, readEntireXmlStream */ String readEntireTextStream (bool usePostCommand = false) const; /** Tries to download the entire contents of this URL and parse it as XML. If it fails, or if the text that it reads can't be parsed as XML, this will return 0. When it returns a valid XmlElement object, the caller is responsibile for deleting this object when no longer needed. Note that on some platforms (Android, for example) it's not permitted to do any network action from the message thread, so you must only call it from a background thread. @param usePostCommand whether to use a POST command to get the data (uses a GET command if this is false) @see readEntireBinaryStream, readEntireTextStream */ XmlElement* readEntireXmlStream (bool usePostCommand = false) const; //============================================================================== /** Adds escape sequences to a string to encode any characters that aren't legal in a URL. E.g. any spaces will be replaced with "%20". This is the opposite of removeEscapeChars(). If isParameter is true, it means that the string is going to be used as a parameter, so it also encodes '$' and ',' (which would otherwise be legal in a URL. @see removeEscapeChars */ static String addEscapeChars (const String& stringToAddEscapeCharsTo, bool isParameter); /** Replaces any escape character sequences in a string with their original character codes. E.g. any instances of "%20" will be replaced by a space. This is the opposite of addEscapeChars(). @see addEscapeChars */ static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom); /** Returns a URL without attempting to remove any embedded parameters from the string. This may be necessary if you need to create a request that involves both POST parameters and parameters which are embedded in the URL address itself. */ static URL createWithoutParsing (const String& url); private: //============================================================================== String url, postData; StringArray parameterNames, parameterValues; struct Upload : public ReferenceCountedObject { Upload (const String&, const String&, const String&, const File&, MemoryBlock*); String parameterName, filename, mimeType; File file; ScopedPointer data; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Upload) }; friend struct ContainerDeletePolicy; ReferenceCountedArray filesToUpload; URL (const String&, int); void addParameter (const String&, const String&); void createHeadersAndPostData (String&, MemoryBlock&) const; URL withUpload (Upload*) const; JUCE_LEAK_DETECTOR (URL) }; #endif // JUCE_URL_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/000077500000000000000000000000001320201440200252205ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp000066400000000000000000000144731320201440200326610ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ namespace { int calcBufferStreamBufferSize (int requestedSize, InputStream* const source) noexcept { // You need to supply a real stream when creating a BufferedInputStream jassert (source != nullptr); requestedSize = jmax (256, requestedSize); const int64 sourceSize = source->getTotalLength(); if (sourceSize >= 0 && sourceSize < requestedSize) requestedSize = jmax (32, (int) sourceSize); return requestedSize; } } //============================================================================== BufferedInputStream::BufferedInputStream (InputStream* const sourceStream, const int bufferSize_, const bool deleteSourceWhenDestroyed) : source (sourceStream, deleteSourceWhenDestroyed), bufferSize (calcBufferStreamBufferSize (bufferSize_, sourceStream)), position (sourceStream->getPosition()), lastReadPos (0), bufferStart (position), bufferOverlap (128) { buffer.malloc ((size_t) bufferSize); } BufferedInputStream::BufferedInputStream (InputStream& sourceStream, const int bufferSize_) : source (&sourceStream, false), bufferSize (calcBufferStreamBufferSize (bufferSize_, &sourceStream)), position (sourceStream.getPosition()), lastReadPos (0), bufferStart (position), bufferOverlap (128) { buffer.malloc ((size_t) bufferSize); } BufferedInputStream::~BufferedInputStream() { } //============================================================================== int64 BufferedInputStream::getTotalLength() { return source->getTotalLength(); } int64 BufferedInputStream::getPosition() { return position; } bool BufferedInputStream::setPosition (int64 newPosition) { position = jmax ((int64) 0, newPosition); return true; } bool BufferedInputStream::isExhausted() { return position >= lastReadPos && source->isExhausted(); } void BufferedInputStream::ensureBuffered() { const int64 bufferEndOverlap = lastReadPos - bufferOverlap; if (position < bufferStart || position >= bufferEndOverlap) { int bytesRead; if (position < lastReadPos && position >= bufferEndOverlap && position >= bufferStart) { const int bytesToKeep = (int) (lastReadPos - position); memmove (buffer, buffer + (int) (position - bufferStart), (size_t) bytesToKeep); bufferStart = position; bytesRead = source->read (buffer + bytesToKeep, (int) (bufferSize - bytesToKeep)); lastReadPos += bytesRead; bytesRead += bytesToKeep; } else { bufferStart = position; source->setPosition (bufferStart); bytesRead = source->read (buffer, bufferSize); lastReadPos = bufferStart + bytesRead; } while (bytesRead < bufferSize) buffer [bytesRead++] = 0; } } int BufferedInputStream::read (void* destBuffer, int maxBytesToRead) { jassert (destBuffer != nullptr && maxBytesToRead >= 0); if (position >= bufferStart && position + maxBytesToRead <= lastReadPos) { memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) maxBytesToRead); position += maxBytesToRead; return maxBytesToRead; } else { if (position < bufferStart || position >= lastReadPos) ensureBuffered(); int bytesRead = 0; while (maxBytesToRead > 0) { const int bytesAvailable = jmin (maxBytesToRead, (int) (lastReadPos - position)); if (bytesAvailable > 0) { memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) bytesAvailable); maxBytesToRead -= bytesAvailable; bytesRead += bytesAvailable; position += bytesAvailable; destBuffer = static_cast (destBuffer) + bytesAvailable; } const int64 oldLastReadPos = lastReadPos; ensureBuffered(); if (oldLastReadPos == lastReadPos) break; // if ensureBuffered() failed to read any more data, bail out if (isExhausted()) break; } return bytesRead; } } String BufferedInputStream::readString() { if (position >= bufferStart && position < lastReadPos) { const int maxChars = (int) (lastReadPos - position); const char* const src = buffer + (int) (position - bufferStart); for (int i = 0; i < maxChars; ++i) { if (src[i] == 0) { position += i + 1; return String::fromUTF8 (src, i); } } } return InputStream::readString(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h000066400000000000000000000077261320201440200323310ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_BUFFEREDINPUTSTREAM_H_INCLUDED #define JUCE_BUFFEREDINPUTSTREAM_H_INCLUDED //============================================================================== /** Wraps another input stream, and reads from it using an intermediate buffer If you're using an input stream such as a file input stream, and making lots of small read accesses to it, it's probably sensible to wrap it in one of these, so that the source stream gets accessed in larger chunk sizes, meaning less work for the underlying stream. */ class JUCE_API BufferedInputStream : public InputStream { public: //============================================================================== /** Creates a BufferedInputStream from an input source. @param sourceStream the source stream to read from @param bufferSize the size of reservoir to use to buffer the source @param deleteSourceWhenDestroyed whether the sourceStream that is passed in should be deleted by this object when it is itself deleted. */ BufferedInputStream (InputStream* sourceStream, int bufferSize, bool deleteSourceWhenDestroyed); /** Creates a BufferedInputStream from an input source. @param sourceStream the source stream to read from - the source stream must not be deleted until this object has been destroyed. @param bufferSize the size of reservoir to use to buffer the source */ BufferedInputStream (InputStream& sourceStream, int bufferSize); /** Destructor. This may also delete the source stream, if that option was chosen when the buffered stream was created. */ ~BufferedInputStream(); //============================================================================== int64 getTotalLength() override; int64 getPosition() override; bool setPosition (int64 newPosition) override; int read (void* destBuffer, int maxBytesToRead) override; String readString() override; bool isExhausted() override; private: //============================================================================== OptionalScopedPointer source; int bufferSize; int64 position, lastReadPos, bufferStart, bufferOverlap; HeapBlock buffer; void ensureBuffered(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferedInputStream) }; #endif // JUCE_BUFFEREDINPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp000066400000000000000000000040141320201440200320110ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ FileInputSource::FileInputSource (const File& f, bool useFileTimeInHash) : file (f), useFileTimeInHashGeneration (useFileTimeInHash) { } FileInputSource::~FileInputSource() { } InputStream* FileInputSource::createInputStream() { return file.createInputStream(); } InputStream* FileInputSource::createInputStreamFor (const String& relatedItemPath) { return file.getSiblingFile (relatedItemPath).createInputStream(); } int64 FileInputSource::hashCode() const { int64 h = file.hashCode(); if (useFileTimeInHashGeneration) h ^= file.getLastModificationTime().toMilliseconds(); return h; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h000066400000000000000000000051461320201440200314650ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_FILEINPUTSOURCE_H_INCLUDED #define JUCE_FILEINPUTSOURCE_H_INCLUDED //============================================================================== /** A type of InputSource that represents a normal file. @see InputSource */ class JUCE_API FileInputSource : public InputSource { public: //============================================================================== /** Creates a FileInputSource for a file. If the useFileTimeInHashGeneration parameter is true, then this object's hashCode() method will incorporate the file time into its hash code; if false, only the file name will be used for the hash. */ FileInputSource (const File& file, bool useFileTimeInHashGeneration = false); /** Destructor. */ ~FileInputSource(); InputStream* createInputStream(); InputStream* createInputStreamFor (const String& relatedItemPath); int64 hashCode() const; private: //============================================================================== const File file; bool useFileTimeInHashGeneration; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputSource) }; #endif // JUCE_FILEINPUTSOURCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h000066400000000000000000000060411320201440200306600ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_INPUTSOURCE_H_INCLUDED #define JUCE_INPUTSOURCE_H_INCLUDED //============================================================================== /** A lightweight object that can create a stream to read some kind of resource. This may be used to refer to a file, or some other kind of source, allowing a caller to create an input stream that can read from it when required. @see FileInputSource */ class JUCE_API InputSource { public: //============================================================================== InputSource() noexcept {} /** Destructor. */ virtual ~InputSource() {} //============================================================================== /** Returns a new InputStream to read this item. @returns an inputstream that the caller will delete, or nullptr if the filename isn't found. */ virtual InputStream* createInputStream() = 0; /** Returns a new InputStream to read an item, relative. @param relatedItemPath the relative pathname of the resource that is required @returns an inputstream that the caller will delete, or nullptr if the item isn't found. */ virtual InputStream* createInputStreamFor (const String& relatedItemPath) = 0; /** Returns a hash code that uniquely represents this item. */ virtual int64 hashCode() const = 0; private: //============================================================================== JUCE_LEAK_DETECTOR (InputSource) }; #endif // JUCE_INPUTSOURCE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp000066400000000000000000000137421320201440200312140ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ int64 InputStream::getNumBytesRemaining() { int64 len = getTotalLength(); if (len >= 0) len -= getPosition(); return len; } char InputStream::readByte() { char temp = 0; read (&temp, 1); return temp; } bool InputStream::readBool() { return readByte() != 0; } short InputStream::readShort() { char temp[2]; if (read (temp, 2) == 2) return (short) ByteOrder::littleEndianShort (temp); return 0; } short InputStream::readShortBigEndian() { char temp[2]; if (read (temp, 2) == 2) return (short) ByteOrder::bigEndianShort (temp); return 0; } int InputStream::readInt() { char temp[4]; if (read (temp, 4) == 4) return (int) ByteOrder::littleEndianInt (temp); return 0; } int InputStream::readIntBigEndian() { char temp[4]; if (read (temp, 4) == 4) return (int) ByteOrder::bigEndianInt (temp); return 0; } int InputStream::readCompressedInt() { const uint8 sizeByte = (uint8) readByte(); if (sizeByte == 0) return 0; const int numBytes = (sizeByte & 0x7f); if (numBytes > 4) { jassertfalse; // trying to read corrupt data - this method must only be used // to read data that was written by OutputStream::writeCompressedInt() return 0; } char bytes[4] = { 0, 0, 0, 0 }; if (read (bytes, numBytes) != numBytes) return 0; const int num = (int) ByteOrder::littleEndianInt (bytes); return (sizeByte >> 7) ? -num : num; } int64 InputStream::readInt64() { union { uint8 asBytes[8]; uint64 asInt64; } n; if (read (n.asBytes, 8) == 8) return (int64) ByteOrder::swapIfBigEndian (n.asInt64); return 0; } int64 InputStream::readInt64BigEndian() { union { uint8 asBytes[8]; uint64 asInt64; } n; if (read (n.asBytes, 8) == 8) return (int64) ByteOrder::swapIfLittleEndian (n.asInt64); return 0; } float InputStream::readFloat() { // the union below relies on these types being the same size... static_jassert (sizeof (int32) == sizeof (float)); union { int32 asInt; float asFloat; } n; n.asInt = (int32) readInt(); return n.asFloat; } float InputStream::readFloatBigEndian() { union { int32 asInt; float asFloat; } n; n.asInt = (int32) readIntBigEndian(); return n.asFloat; } double InputStream::readDouble() { union { int64 asInt; double asDouble; } n; n.asInt = readInt64(); return n.asDouble; } double InputStream::readDoubleBigEndian() { union { int64 asInt; double asDouble; } n; n.asInt = readInt64BigEndian(); return n.asDouble; } String InputStream::readString() { MemoryBlock buffer (256); char* data = static_cast (buffer.getData()); size_t i = 0; while ((data[i] = readByte()) != 0) { if (++i >= buffer.getSize()) { buffer.setSize (buffer.getSize() + 512); data = static_cast (buffer.getData()); } } return String::fromUTF8 (data, (int) i); } String InputStream::readNextLine() { MemoryBlock buffer (256); char* data = static_cast (buffer.getData()); size_t i = 0; while ((data[i] = readByte()) != 0) { if (data[i] == '\n') break; if (data[i] == '\r') { const int64 lastPos = getPosition(); if (readByte() != '\n') setPosition (lastPos); break; } if (++i >= buffer.getSize()) { buffer.setSize (buffer.getSize() + 512); data = static_cast (buffer.getData()); } } return String::fromUTF8 (data, (int) i); } size_t InputStream::readIntoMemoryBlock (MemoryBlock& block, ssize_t numBytes) { MemoryOutputStream mo (block, true); return (size_t) mo.writeFromInputStream (*this, numBytes); } String InputStream::readEntireStreamAsString() { MemoryOutputStream mo; mo << *this; return mo.toString(); } //============================================================================== void InputStream::skipNextBytes (int64 numBytesToSkip) { if (numBytesToSkip > 0) { const int skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384); HeapBlock temp ((size_t) skipBufferSize); while (numBytesToSkip > 0 && ! isExhausted()) numBytesToSkip -= read (temp, (int) jmin (numBytesToSkip, (int64) skipBufferSize)); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h000066400000000000000000000265111320201440200306570ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_INPUTSTREAM_H_INCLUDED #define JUCE_INPUTSTREAM_H_INCLUDED //============================================================================== /** The base class for streams that read data. Input and output streams are used throughout the library - subclasses can override some or all of the virtual functions to implement their behaviour. @see OutputStream, MemoryInputStream, BufferedInputStream, FileInputStream */ class JUCE_API InputStream { public: /** Destructor. */ virtual ~InputStream() {} //============================================================================== /** Returns the total number of bytes available for reading in this stream. Note that this is the number of bytes available from the start of the stream, not from the current position. If the size of the stream isn't actually known, this will return -1. @see getNumBytesRemaining */ virtual int64 getTotalLength() = 0; /** Returns the number of bytes available for reading, or a negative value if the remaining length is not known. @see getTotalLength */ int64 getNumBytesRemaining(); /** Returns true if the stream has no more data to read. */ virtual bool isExhausted() = 0; //============================================================================== /** Reads some data from the stream into a memory buffer. This is the only read method that subclasses actually need to implement, as the InputStream base class implements the other read methods in terms of this one (although it's often more efficient for subclasses to implement them directly). @param destBuffer the destination buffer for the data. This must not be null. @param maxBytesToRead the maximum number of bytes to read - make sure the memory block passed in is big enough to contain this many bytes. This value must not be negative. @returns the actual number of bytes that were read, which may be less than maxBytesToRead if the stream is exhausted before it gets that far */ virtual int read (void* destBuffer, int maxBytesToRead) = 0; /** Reads a byte from the stream. If the stream is exhausted, this will return zero. @see OutputStream::writeByte */ virtual char readByte(); /** Reads a boolean from the stream. The bool is encoded as a single byte - non-zero for true, 0 for false. If the stream is exhausted, this will return false. @see OutputStream::writeBool */ virtual bool readBool(); /** Reads two bytes from the stream as a little-endian 16-bit value. If the next two bytes read are byte1 and byte2, this returns (byte1 | (byte2 << 8)). If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeShort, readShortBigEndian */ virtual short readShort(); /** Reads two bytes from the stream as a little-endian 16-bit value. If the next two bytes read are byte1 and byte2, this returns (byte2 | (byte1 << 8)). If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeShortBigEndian, readShort */ virtual short readShortBigEndian(); /** Reads four bytes from the stream as a little-endian 32-bit value. If the next four bytes are byte1 to byte4, this returns (byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24)). If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeInt, readIntBigEndian */ virtual int readInt(); /** Reads four bytes from the stream as a big-endian 32-bit value. If the next four bytes are byte1 to byte4, this returns (byte4 | (byte3 << 8) | (byte2 << 16) | (byte1 << 24)). If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeIntBigEndian, readInt */ virtual int readIntBigEndian(); /** Reads eight bytes from the stream as a little-endian 64-bit value. If the next eight bytes are byte1 to byte8, this returns (byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24) | (byte5 << 32) | (byte6 << 40) | (byte7 << 48) | (byte8 << 56)). If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeInt64, readInt64BigEndian */ virtual int64 readInt64(); /** Reads eight bytes from the stream as a big-endian 64-bit value. If the next eight bytes are byte1 to byte8, this returns (byte8 | (byte7 << 8) | (byte6 << 16) | (byte5 << 24) | (byte4 << 32) | (byte3 << 40) | (byte2 << 48) | (byte1 << 56)). If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeInt64BigEndian, readInt64 */ virtual int64 readInt64BigEndian(); /** Reads four bytes as a 32-bit floating point value. The raw 32-bit encoding of the float is read from the stream as a little-endian int. If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeFloat, readDouble */ virtual float readFloat(); /** Reads four bytes as a 32-bit floating point value. The raw 32-bit encoding of the float is read from the stream as a big-endian int. If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeFloatBigEndian, readDoubleBigEndian */ virtual float readFloatBigEndian(); /** Reads eight bytes as a 64-bit floating point value. The raw 64-bit encoding of the double is read from the stream as a little-endian int64. If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeDouble, readFloat */ virtual double readDouble(); /** Reads eight bytes as a 64-bit floating point value. The raw 64-bit encoding of the double is read from the stream as a big-endian int64. If the stream is exhausted partway through reading the bytes, this will return zero. @see OutputStream::writeDoubleBigEndian, readFloatBigEndian */ virtual double readDoubleBigEndian(); /** Reads an encoded 32-bit number from the stream using a space-saving compressed format. For small values, this is more space-efficient than using readInt() and OutputStream::writeInt() The format used is: number of significant bytes + up to 4 bytes in little-endian order. @see OutputStream::writeCompressedInt() */ virtual int readCompressedInt(); //============================================================================== /** Reads a UTF-8 string from the stream, up to the next linefeed or carriage return. This will read up to the next "\n" or "\r\n" or end-of-stream. After this call, the stream's position will be left pointing to the next character following the line-feed, but the linefeeds aren't included in the string that is returned. */ virtual String readNextLine(); /** Reads a zero-terminated UTF-8 string from the stream. This will read characters from the stream until it hits a null character or end-of-stream. @see OutputStream::writeString, readEntireStreamAsString */ virtual String readString(); /** Tries to read the whole stream and turn it into a string. This will read from the stream's current position until the end-of-stream. It can read from UTF-8 data, or UTF-16 if it detects suitable header-bytes. */ virtual String readEntireStreamAsString(); /** Reads from the stream and appends the data to a MemoryBlock. @param destBlock the block to append the data onto @param maxNumBytesToRead if this is a positive value, it sets a limit to the number of bytes that will be read - if it's negative, data will be read until the stream is exhausted. @returns the number of bytes that were added to the memory block */ virtual size_t readIntoMemoryBlock (MemoryBlock& destBlock, ssize_t maxNumBytesToRead = -1); //============================================================================== /** Returns the offset of the next byte that will be read from the stream. @see setPosition */ virtual int64 getPosition() = 0; /** Tries to move the current read position of the stream. The position is an absolute number of bytes from the stream's start. Some streams might not be able to do this, in which case they should do nothing and return false. Others might be able to manage it by resetting themselves and skipping to the correct position, although this is obviously a bit slow. @returns true if the stream manages to reposition itself correctly @see getPosition */ virtual bool setPosition (int64 newPosition) = 0; /** Reads and discards a number of bytes from the stream. Some input streams might implement this efficiently, but the base class will just keep reading data until the requisite number of bytes have been done. */ virtual void skipNextBytes (int64 numBytesToSkip); protected: //============================================================================== InputStream() noexcept {} private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InputStream) }; #endif // JUCE_INPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp000066400000000000000000000117571320201440200324110ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ MemoryInputStream::MemoryInputStream (const void* const sourceData, const size_t sourceDataSize, const bool keepInternalCopy) : data (sourceData), dataSize (sourceDataSize), position (0) { if (keepInternalCopy) createInternalCopy(); } MemoryInputStream::MemoryInputStream (const MemoryBlock& sourceData, const bool keepInternalCopy) : data (sourceData.getData()), dataSize (sourceData.getSize()), position (0) { if (keepInternalCopy) createInternalCopy(); } void MemoryInputStream::createInternalCopy() { internalCopy.malloc (dataSize); memcpy (internalCopy, data, dataSize); data = internalCopy; } MemoryInputStream::~MemoryInputStream() { } int64 MemoryInputStream::getTotalLength() { return (int64) dataSize; } int MemoryInputStream::read (void* const buffer, const int howMany) { jassert (buffer != nullptr && howMany >= 0); const int num = jmin (howMany, (int) (dataSize - position)); if (num <= 0) return 0; memcpy (buffer, addBytesToPointer (data, position), (size_t) num); position += (unsigned int) num; return num; } bool MemoryInputStream::isExhausted() { return position >= dataSize; } bool MemoryInputStream::setPosition (const int64 pos) { position = (size_t) jlimit ((int64) 0, (int64) dataSize, pos); return true; } int64 MemoryInputStream::getPosition() { return (int64) position; } //============================================================================== #if JUCE_UNIT_TESTS class MemoryStreamTests : public UnitTest { public: MemoryStreamTests() : UnitTest ("MemoryInputStream & MemoryOutputStream") {} void runTest() { beginTest ("Basics"); Random r = getRandom(); int randomInt = r.nextInt(); int64 randomInt64 = r.nextInt64(); double randomDouble = r.nextDouble(); String randomString (createRandomWideCharString (r)); MemoryOutputStream mo; mo.writeInt (randomInt); mo.writeIntBigEndian (randomInt); mo.writeCompressedInt (randomInt); mo.writeString (randomString); mo.writeInt64 (randomInt64); mo.writeInt64BigEndian (randomInt64); mo.writeDouble (randomDouble); mo.writeDoubleBigEndian (randomDouble); MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); expect (mi.readInt() == randomInt); expect (mi.readIntBigEndian() == randomInt); expect (mi.readCompressedInt() == randomInt); expectEquals (mi.readString(), randomString); expect (mi.readInt64() == randomInt64); expect (mi.readInt64BigEndian() == randomInt64); expect (mi.readDouble() == randomDouble); expect (mi.readDoubleBigEndian() == randomDouble); } static String createRandomWideCharString (Random& r) { juce_wchar buffer [50] = { 0 }; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { if (r.nextBool()) { do { buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); } while (! CharPointer_UTF16::canRepresent (buffer[i])); } else buffer[i] = (juce_wchar) (1 + r.nextInt (0xff)); } return CharPointer_UTF32 (buffer); } }; static MemoryStreamTests memoryInputStreamUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h000066400000000000000000000105511320201440200320450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_MEMORYINPUTSTREAM_H_INCLUDED #define JUCE_MEMORYINPUTSTREAM_H_INCLUDED //============================================================================== /** Allows a block of data to be accessed as a stream. This can either be used to refer to a shared block of memory, or can make its own internal copy of the data when the MemoryInputStream is created. */ class JUCE_API MemoryInputStream : public InputStream { public: //============================================================================== /** Creates a MemoryInputStream. @param sourceData the block of data to use as the stream's source @param sourceDataSize the number of bytes in the source data block @param keepInternalCopyOfData if false, the stream will just keep a pointer to the source data, so this data shouldn't be changed for the lifetime of the stream; if this parameter is true, the stream will make its own copy of the data and use that. */ MemoryInputStream (const void* sourceData, size_t sourceDataSize, bool keepInternalCopyOfData); /** Creates a MemoryInputStream. @param data a block of data to use as the stream's source @param keepInternalCopyOfData if false, the stream will just keep a reference to the source data, so this data shouldn't be changed for the lifetime of the stream; if this parameter is true, the stream will make its own copy of the data and use that. */ MemoryInputStream (const MemoryBlock& data, bool keepInternalCopyOfData); /** Destructor. */ ~MemoryInputStream(); /** Returns a pointer to the source data block from which this stream is reading. */ const void* getData() const noexcept { return data; } /** Returns the number of bytes of source data in the block from which this stream is reading. */ size_t getDataSize() const noexcept { return dataSize; } //============================================================================== int64 getPosition() override; bool setPosition (int64 pos) override; int64 getTotalLength() override; bool isExhausted() override; int read (void* destBuffer, int maxBytesToRead) override; private: //============================================================================== const void* data; size_t dataSize, position; HeapBlock internalCopy; void createInternalCopy(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryInputStream) }; #endif // JUCE_MEMORYINPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp000066400000000000000000000143451320201440200326060ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ MemoryOutputStream::MemoryOutputStream (const size_t initialSize) : blockToUse (&internalBlock), externalData (nullptr), position (0), size (0), availableSize (0) { internalBlock.setSize (initialSize, false); } MemoryOutputStream::MemoryOutputStream (MemoryBlock& memoryBlockToWriteTo, const bool appendToExistingBlockContent) : blockToUse (&memoryBlockToWriteTo), externalData (nullptr), position (0), size (0), availableSize (0) { if (appendToExistingBlockContent) position = size = memoryBlockToWriteTo.getSize(); } MemoryOutputStream::MemoryOutputStream (void* destBuffer, size_t destBufferSize) : blockToUse (nullptr), externalData (destBuffer), position (0), size (0), availableSize (destBufferSize) { jassert (externalData != nullptr); // This must be a valid pointer. } MemoryOutputStream::~MemoryOutputStream() { trimExternalBlockSize(); } void MemoryOutputStream::flush() { trimExternalBlockSize(); } void MemoryOutputStream::trimExternalBlockSize() { if (blockToUse != &internalBlock && blockToUse != nullptr) blockToUse->setSize (size, false); } void MemoryOutputStream::preallocate (const size_t bytesToPreallocate) { if (blockToUse != nullptr) blockToUse->ensureSize (bytesToPreallocate + 1); } void MemoryOutputStream::reset() noexcept { position = 0; size = 0; } char* MemoryOutputStream::prepareToWrite (size_t numBytes) { jassert ((ssize_t) numBytes >= 0); size_t storageNeeded = position + numBytes; char* data; if (blockToUse != nullptr) { if (storageNeeded >= blockToUse->getSize()) blockToUse->ensureSize ((storageNeeded + jmin (storageNeeded / 2, (size_t) (1024 * 1024)) + 32) & ~31u); data = static_cast (blockToUse->getData()); } else { if (storageNeeded > availableSize) return nullptr; data = static_cast (externalData); } char* const writePointer = data + position; position += numBytes; size = jmax (size, position); return writePointer; } bool MemoryOutputStream::write (const void* const buffer, size_t howMany) { jassert (buffer != nullptr); if (howMany == 0) return true; if (char* dest = prepareToWrite (howMany)) { memcpy (dest, buffer, howMany); return true; } return false; } bool MemoryOutputStream::writeRepeatedByte (uint8 byte, size_t howMany) { if (howMany == 0) return true; if (char* dest = prepareToWrite (howMany)) { memset (dest, byte, howMany); return true; } return false; } bool MemoryOutputStream::appendUTF8Char (juce_wchar c) { if (char* dest = prepareToWrite (CharPointer_UTF8::getBytesRequiredFor (c))) { CharPointer_UTF8 (dest).write (c); return true; } return false; } MemoryBlock MemoryOutputStream::getMemoryBlock() const { return MemoryBlock (getData(), getDataSize()); } const void* MemoryOutputStream::getData() const noexcept { if (blockToUse == nullptr) return externalData; if (blockToUse->getSize() > size) static_cast (blockToUse->getData()) [size] = 0; return blockToUse->getData(); } bool MemoryOutputStream::setPosition (int64 newPosition) { if (newPosition <= (int64) size) { // ok to seek backwards position = jlimit ((size_t) 0, size, (size_t) newPosition); return true; } // can't move beyond the end of the stream.. return false; } int64 MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite) { // before writing from an input, see if we can preallocate to make it more efficient.. int64 availableData = source.getTotalLength() - source.getPosition(); if (availableData > 0) { if (maxNumBytesToWrite > availableData || maxNumBytesToWrite < 0) maxNumBytesToWrite = availableData; if (blockToUse != nullptr) preallocate (blockToUse->getSize() + (size_t) maxNumBytesToWrite); } return OutputStream::writeFromInputStream (source, maxNumBytesToWrite); } String MemoryOutputStream::toUTF8() const { const char* const d = static_cast (getData()); return String (CharPointer_UTF8 (d), CharPointer_UTF8 (d + getDataSize())); } String MemoryOutputStream::toString() const { return String::createStringFromData (getData(), (int) getDataSize()); } OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryOutputStream& streamToRead) { const size_t dataSize = streamToRead.getDataSize(); if (dataSize > 0) stream.write (streamToRead.getData(), dataSize); return stream; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h000066400000000000000000000137601320201440200322530ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED #define JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED //============================================================================== /** Writes data to an internal memory buffer, which grows as required. The data that was written into the stream can then be accessed later as a contiguous block of memory. */ class JUCE_API MemoryOutputStream : public OutputStream { public: //============================================================================== /** Creates an empty memory stream, ready to be written into. @param initialSize the intial amount of capacity to allocate for writing into */ MemoryOutputStream (size_t initialSize = 256); /** Creates a memory stream for writing into into a pre-existing MemoryBlock object. Note that the destination block will always be larger than the amount of data that has been written to the stream, because the MemoryOutputStream keeps some spare capactity at its end. To trim the block's size down to fit the actual data, call flush(), or delete the MemoryOutputStream. @param memoryBlockToWriteTo the block into which new data will be written. @param appendToExistingBlockContent if this is true, the contents of the block will be kept, and new data will be appended to it. If false, the block will be cleared before use */ MemoryOutputStream (MemoryBlock& memoryBlockToWriteTo, bool appendToExistingBlockContent); /** Creates a MemoryOutputStream that will write into a user-supplied, fixed-size block of memory. When using this mode, the stream will write directly into this memory area until it's full, at which point write operations will fail. */ MemoryOutputStream (void* destBuffer, size_t destBufferSize); /** Destructor. This will free any data that was written to it. */ ~MemoryOutputStream(); //============================================================================== /** Returns a pointer to the data that has been written to the stream. @see getDataSize */ const void* getData() const noexcept; /** Returns the number of bytes of data that have been written to the stream. @see getData */ size_t getDataSize() const noexcept { return size; } /** Resets the stream, clearing any data that has been written to it so far. */ void reset() noexcept; /** Increases the internal storage capacity to be able to contain at least the specified amount of data without needing to be resized. */ void preallocate (size_t bytesToPreallocate); /** Appends the utf-8 bytes for a unicode character */ bool appendUTF8Char (juce_wchar character); /** Returns a String created from the (UTF8) data that has been written to the stream. */ String toUTF8() const; /** Attempts to detect the encoding of the data and convert it to a string. @see String::createStringFromData */ String toString() const; /** Returns a copy of the stream's data as a memory block. */ MemoryBlock getMemoryBlock() const; //============================================================================== /** If the stream is writing to a user-supplied MemoryBlock, this will trim any excess capacity off the block, so that its length matches the amount of actual data that has been written so far. */ void flush() override; bool write (const void*, size_t) override; int64 getPosition() override { return (int64) position; } bool setPosition (int64) override; int64 writeFromInputStream (InputStream&, int64 maxNumBytesToWrite) override; bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override; private: //============================================================================== MemoryBlock* const blockToUse; MemoryBlock internalBlock; void* externalData; size_t position, size, availableSize; void trimExternalBlockSize(); char* prepareToWrite (size_t); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryOutputStream) }; /** Copies all the data that has been written to a MemoryOutputStream into another stream. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryOutputStream& streamToRead); #endif // JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp000066400000000000000000000231101320201440200314030ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #if JUCE_DEBUG struct DanglingStreamChecker { DanglingStreamChecker() {} ~DanglingStreamChecker() { /* It's always a bad idea to leak any object, but if you're leaking output streams, then there's a good chance that you're failing to flush a file to disk properly, which could result in corrupted data and other similar nastiness.. */ jassert (activeStreams.size() == 0); } Array activeStreams; }; static DanglingStreamChecker danglingStreamChecker; #endif //============================================================================== OutputStream::OutputStream() : newLineString (NewLine::getDefault()) { #if JUCE_DEBUG danglingStreamChecker.activeStreams.add (this); #endif } OutputStream::~OutputStream() { #if JUCE_DEBUG danglingStreamChecker.activeStreams.removeFirstMatchingValue (this); #endif } //============================================================================== bool OutputStream::writeBool (const bool b) { return writeByte (b ? (char) 1 : (char) 0); } bool OutputStream::writeByte (char byte) { return write (&byte, 1); } bool OutputStream::writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) { for (size_t i = 0; i < numTimesToRepeat; ++i) if (! writeByte ((char) byte)) return false; return true; } bool OutputStream::writeShort (short value) { const unsigned short v = ByteOrder::swapIfBigEndian ((unsigned short) value); return write (&v, 2); } bool OutputStream::writeShortBigEndian (short value) { const unsigned short v = ByteOrder::swapIfLittleEndian ((unsigned short) value); return write (&v, 2); } bool OutputStream::writeInt (int value) { const unsigned int v = ByteOrder::swapIfBigEndian ((unsigned int) value); return write (&v, 4); } bool OutputStream::writeIntBigEndian (int value) { const unsigned int v = ByteOrder::swapIfLittleEndian ((unsigned int) value); return write (&v, 4); } bool OutputStream::writeCompressedInt (int value) { unsigned int un = (value < 0) ? (unsigned int) -value : (unsigned int) value; uint8 data[5]; int num = 0; while (un > 0) { data[++num] = (uint8) un; un >>= 8; } data[0] = (uint8) num; if (value < 0) data[0] |= 0x80; return write (data, (size_t) num + 1); } bool OutputStream::writeInt64 (int64 value) { const uint64 v = ByteOrder::swapIfBigEndian ((uint64) value); return write (&v, 8); } bool OutputStream::writeInt64BigEndian (int64 value) { const uint64 v = ByteOrder::swapIfLittleEndian ((uint64) value); return write (&v, 8); } bool OutputStream::writeFloat (float value) { union { int asInt; float asFloat; } n; n.asFloat = value; return writeInt (n.asInt); } bool OutputStream::writeFloatBigEndian (float value) { union { int asInt; float asFloat; } n; n.asFloat = value; return writeIntBigEndian (n.asInt); } bool OutputStream::writeDouble (double value) { union { int64 asInt; double asDouble; } n; n.asDouble = value; return writeInt64 (n.asInt); } bool OutputStream::writeDoubleBigEndian (double value) { union { int64 asInt; double asDouble; } n; n.asDouble = value; return writeInt64BigEndian (n.asInt); } bool OutputStream::writeString (const String& text) { #if (JUCE_STRING_UTF_TYPE == 8) return write (text.toRawUTF8(), text.getNumBytesAsUTF8() + 1); #else // (This avoids using toUTF8() to prevent the memory bloat that it would leave behind // if lots of large, persistent strings were to be written to streams). const size_t numBytes = text.getNumBytesAsUTF8() + 1; HeapBlock temp (numBytes); text.copyToUTF8 (temp, numBytes); return write (temp, numBytes); #endif } bool OutputStream::writeText (const String& text, const bool asUTF16, const bool writeUTF16ByteOrderMark) { if (asUTF16) { if (writeUTF16ByteOrderMark) write ("\x0ff\x0fe", 2); String::CharPointerType src (text.getCharPointer()); bool lastCharWasReturn = false; for (;;) { const juce_wchar c = src.getAndAdvance(); if (c == 0) break; if (c == '\n' && ! lastCharWasReturn) writeShort ((short) '\r'); lastCharWasReturn = (c == L'\r'); if (! writeShort ((short) c)) return false; } } else { const char* src = text.toUTF8(); const char* t = src; for (;;) { if (*t == '\n') { if (t > src) if (! write (src, (size_t) (t - src))) return false; if (! write ("\r\n", 2)) return false; src = t + 1; } else if (*t == '\r') { if (t[1] == '\n') ++t; } else if (*t == 0) { if (t > src) if (! write (src, (size_t) (t - src))) return false; break; } ++t; } } return true; } int64 OutputStream::writeFromInputStream (InputStream& source, int64 numBytesToWrite) { if (numBytesToWrite < 0) numBytesToWrite = std::numeric_limits::max(); int64 numWritten = 0; while (numBytesToWrite > 0) { char buffer [8192]; const int num = source.read (buffer, (int) jmin (numBytesToWrite, (int64) sizeof (buffer))); if (num <= 0) break; write (buffer, (size_t) num); numBytesToWrite -= num; numWritten += num; } return numWritten; } //============================================================================== void OutputStream::setNewLineString (const String& newLineString_) { newLineString = newLineString_; } //============================================================================== template static void writeIntToStream (OutputStream& stream, IntegerType number) { char buffer [NumberToStringConverters::charsNeededForInt]; char* end = buffer + numElementsInArray (buffer); const char* start = NumberToStringConverters::numberToString (end, number); stream.write (start, (size_t) (end - start - 1)); } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int number) { writeIntToStream (stream, number); return stream; } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int64 number) { writeIntToStream (stream, number); return stream; } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const double number) { return stream << String (number); } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char character) { stream.writeByte (character); return stream; } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char* const text) { stream.write (text, strlen (text)); return stream; } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data) { if (data.getSize() > 0) stream.write (data.getData(), data.getSize()); return stream; } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead) { FileInputStream in (fileToRead); if (in.openedOk()) return stream << in; return stream; } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead) { stream.writeFromInputStream (streamToRead, -1); return stream; } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&) { return stream << stream.getNewLineString(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h000066400000000000000000000274671320201440200310730ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_OUTPUTSTREAM_H_INCLUDED #define JUCE_OUTPUTSTREAM_H_INCLUDED //============================================================================== /** The base class for streams that write data to some kind of destination. Input and output streams are used throughout the library - subclasses can override some or all of the virtual functions to implement their behaviour. @see InputStream, MemoryOutputStream, FileOutputStream */ class JUCE_API OutputStream { protected: //============================================================================== OutputStream(); public: /** Destructor. Some subclasses might want to do things like call flush() during their destructors. */ virtual ~OutputStream(); //============================================================================== /** If the stream is using a buffer, this will ensure it gets written out to the destination. */ virtual void flush() = 0; /** Tries to move the stream's output position. Not all streams will be able to seek to a new position - this will return false if it fails to work. @see getPosition */ virtual bool setPosition (int64 newPosition) = 0; /** Returns the stream's current position. @see setPosition */ virtual int64 getPosition() = 0; //============================================================================== /** Writes a block of data to the stream. When creating a subclass of OutputStream, this is the only write method that needs to be overloaded - the base class has methods for writing other types of data which use this to do the work. @param dataToWrite the target buffer to receive the data. This must not be null. @param numberOfBytes the number of bytes to write. @returns false if the write operation fails for some reason */ virtual bool write (const void* dataToWrite, size_t numberOfBytes) = 0; //============================================================================== /** Writes a single byte to the stream. @returns false if the write operation fails for some reason @see InputStream::readByte */ virtual bool writeByte (char byte); /** Writes a boolean to the stream as a single byte. This is encoded as a binary byte (not as text) with a value of 1 or 0. @returns false if the write operation fails for some reason @see InputStream::readBool */ virtual bool writeBool (bool boolValue); /** Writes a 16-bit integer to the stream in a little-endian byte order. This will write two bytes to the stream: (value & 0xff), then (value >> 8). @returns false if the write operation fails for some reason @see InputStream::readShort */ virtual bool writeShort (short value); /** Writes a 16-bit integer to the stream in a big-endian byte order. This will write two bytes to the stream: (value >> 8), then (value & 0xff). @returns false if the write operation fails for some reason @see InputStream::readShortBigEndian */ virtual bool writeShortBigEndian (short value); /** Writes a 32-bit integer to the stream in a little-endian byte order. @returns false if the write operation fails for some reason @see InputStream::readInt */ virtual bool writeInt (int value); /** Writes a 32-bit integer to the stream in a big-endian byte order. @returns false if the write operation fails for some reason @see InputStream::readIntBigEndian */ virtual bool writeIntBigEndian (int value); /** Writes a 64-bit integer to the stream in a little-endian byte order. @returns false if the write operation fails for some reason @see InputStream::readInt64 */ virtual bool writeInt64 (int64 value); /** Writes a 64-bit integer to the stream in a big-endian byte order. @returns false if the write operation fails for some reason @see InputStream::readInt64BigEndian */ virtual bool writeInt64BigEndian (int64 value); /** Writes a 32-bit floating point value to the stream in a binary format. The binary 32-bit encoding of the float is written as a little-endian int. @returns false if the write operation fails for some reason @see InputStream::readFloat */ virtual bool writeFloat (float value); /** Writes a 32-bit floating point value to the stream in a binary format. The binary 32-bit encoding of the float is written as a big-endian int. @returns false if the write operation fails for some reason @see InputStream::readFloatBigEndian */ virtual bool writeFloatBigEndian (float value); /** Writes a 64-bit floating point value to the stream in a binary format. The eight raw bytes of the double value are written out as a little-endian 64-bit int. @returns false if the write operation fails for some reason @see InputStream::readDouble */ virtual bool writeDouble (double value); /** Writes a 64-bit floating point value to the stream in a binary format. The eight raw bytes of the double value are written out as a big-endian 64-bit int. @see InputStream::readDoubleBigEndian @returns false if the write operation fails for some reason */ virtual bool writeDoubleBigEndian (double value); /** Writes a byte to the output stream a given number of times. @returns false if the write operation fails for some reason */ virtual bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat); /** Writes a condensed binary encoding of a 32-bit integer. If you're storing a lot of integers which are unlikely to have very large values, this can save a lot of space, because values under 0xff will only take up 2 bytes, under 0xffff only 3 bytes, etc. The format used is: number of significant bytes + up to 4 bytes in little-endian order. @returns false if the write operation fails for some reason @see InputStream::readCompressedInt */ virtual bool writeCompressedInt (int value); /** Stores a string in the stream in a binary format. This isn't the method to use if you're trying to append text to the end of a text-file! It's intended for storing a string so that it can be retrieved later by InputStream::readString(). It writes the string to the stream as UTF8, including the null termination character. For appending text to a file, instead use writeText, or operator<< @returns false if the write operation fails for some reason @see InputStream::readString, writeText, operator<< */ virtual bool writeString (const String& text); /** Writes a string of text to the stream. It can either write the text as UTF-8 or UTF-16, and can also add the UTF-16 byte-order-mark bytes (0xff, 0xfe) to indicate the endianness (these should only be used at the start of a file). The method also replaces '\\n' characters in the text with '\\r\\n'. @returns false if the write operation fails for some reason */ virtual bool writeText (const String& text, bool asUTF16, bool writeUTF16ByteOrderMark); /** Reads data from an input stream and writes it to this stream. @param source the stream to read from @param maxNumBytesToWrite the number of bytes to read from the stream (if this is less than zero, it will keep reading until the input is exhausted) @returns the number of bytes written */ virtual int64 writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite); //============================================================================== /** Sets the string that will be written to the stream when the writeNewLine() method is called. By default this will be set the value of NewLine::getDefault(). */ void setNewLineString (const String& newLineString); /** Returns the current new-line string that was set by setNewLineString(). */ const String& getNewLineString() const noexcept { return newLineString; } private: //============================================================================== String newLineString; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OutputStream) }; //============================================================================== /** Writes a number to a stream as 8-bit characters in the default system encoding. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, int number); /** Writes a number to a stream as 8-bit characters in the default system encoding. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, int64 number); /** Writes a number to a stream as 8-bit characters in the default system encoding. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, double number); /** Writes a character to a stream. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, char character); /** Writes a null-terminated text string to a stream. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char* text); /** Writes a block of data from a MemoryBlock to a stream. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data); /** Writes the contents of a file to a stream. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead); /** Writes the complete contents of an input stream to an output stream. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead); /** Writes a new-line to a stream. You can use the predefined symbol 'newLine' to invoke this, e.g. @code myOutputStream << "Hello World" << newLine << newLine; @endcode @see OutputStream::setNewLineString */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&); #endif // JUCE_OUTPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp000066400000000000000000000056571320201440200320600ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ SubregionStream::SubregionStream (InputStream* const sourceStream, const int64 start, const int64 length, const bool deleteSourceWhenDestroyed) : source (sourceStream, deleteSourceWhenDestroyed), startPositionInSourceStream (start), lengthOfSourceStream (length) { SubregionStream::setPosition (0); } SubregionStream::~SubregionStream() { } int64 SubregionStream::getTotalLength() { const int64 srcLen = source->getTotalLength() - startPositionInSourceStream; return lengthOfSourceStream >= 0 ? jmin (lengthOfSourceStream, srcLen) : srcLen; } int64 SubregionStream::getPosition() { return source->getPosition() - startPositionInSourceStream; } bool SubregionStream::setPosition (int64 newPosition) { return source->setPosition (jmax ((int64) 0, newPosition + startPositionInSourceStream)); } int SubregionStream::read (void* destBuffer, int maxBytesToRead) { jassert (destBuffer != nullptr && maxBytesToRead >= 0); if (lengthOfSourceStream < 0) return source->read (destBuffer, maxBytesToRead); maxBytesToRead = (int) jmin ((int64) maxBytesToRead, lengthOfSourceStream - getPosition()); if (maxBytesToRead <= 0) return 0; return source->read (destBuffer, maxBytesToRead); } bool SubregionStream::isExhausted() { if (lengthOfSourceStream >= 0 && getPosition() >= lengthOfSourceStream) return true; return source->isExhausted(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h000066400000000000000000000103521320201440200315110ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SUBREGIONSTREAM_H_INCLUDED #define JUCE_SUBREGIONSTREAM_H_INCLUDED //============================================================================== /** Wraps another input stream, and reads from a specific part of it. This lets you take a subsection of a stream and present it as an entire stream in its own right. */ class JUCE_API SubregionStream : public InputStream { public: //============================================================================== /** Creates a SubregionStream from an input source. @param sourceStream the source stream to read from @param startPositionInSourceStream this is the position in the source stream that corresponds to position 0 in this stream @param lengthOfSourceStream this specifies the maximum number of bytes from the source stream that will be passed through by this stream. When the position of this stream exceeds lengthOfSourceStream, it will cause an end-of-stream. If the length passed in here is greater than the length of the source stream (as returned by getTotalLength()), then the smaller value will be used. Passing a negative value for this parameter means it will keep reading until the source's end-of-stream. @param deleteSourceWhenDestroyed whether the sourceStream that is passed in should be deleted by this object when it is itself deleted. */ SubregionStream (InputStream* sourceStream, int64 startPositionInSourceStream, int64 lengthOfSourceStream, bool deleteSourceWhenDestroyed); /** Destructor. This may also delete the source stream, if that option was chosen when the buffered stream was created. */ ~SubregionStream(); //============================================================================== int64 getTotalLength() override; int64 getPosition() override; bool setPosition (int64 newPosition) override; int read (void* destBuffer, int maxBytesToRead) override; bool isExhausted() override; private: //============================================================================== OptionalScopedPointer source; const int64 startPositionInSourceStream, lengthOfSourceStream; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubregionStream) }; #endif // JUCE_SUBREGIONSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/system/000077500000000000000000000000001320201440200250665ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h000066400000000000000000000124021320201440200314130ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_COMPILERSUPPORT_H_INCLUDED #define JUCE_COMPILERSUPPORT_H_INCLUDED /* This file has some checks to see whether the compiler supports various C++11/14 features, When these aren't available, the code defines a few workarounds, so that we can still use some of the newer language features like nullptr and noexcept, even on old compilers. */ //============================================================================== // GCC #if (__cplusplus >= 201103L || defined (__GXX_EXPERIMENTAL_CXX0X__)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 #define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1 #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL) #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 #endif #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_DELETED_FUNCTION) #define JUCE_DELETED_FUNCTION = delete #endif #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && ! defined (JUCE_COMPILER_SUPPORTS_LAMBDAS) #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 #endif #endif //============================================================================== // Clang #if JUCE_CLANG && defined (__has_feature) #if __has_feature (cxx_nullptr) #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 #endif #if __has_feature (cxx_noexcept) #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 #endif #if __has_feature (cxx_rvalue_references) #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 #endif #if __has_feature (cxx_deleted_functions) #define JUCE_DELETED_FUNCTION = delete #endif #if __has_feature (cxx_lambdas) && (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 #endif #if __has_feature (cxx_generalized_initializers) && (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) #define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1 #endif #if __has_feature (cxx_variadic_templates) #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 #endif #ifndef JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 #endif #ifndef JUCE_COMPILER_SUPPORTS_ARC #define JUCE_COMPILER_SUPPORTS_ARC 1 #endif #endif //============================================================================== // MSVC #ifdef _MSC_VER #if _MSC_VER >= 1600 #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 #endif #if _MSC_VER >= 1700 #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 #endif #if _MSC_VER >= 1800 #define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1 #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 #define JUCE_DELETED_FUNCTION = delete #endif #if _MSC_VER >= 1900 #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 #endif #endif //============================================================================== // Declare some fake versions of nullptr and noexcept, for older compilers: #ifndef JUCE_DELETED_FUNCTION /** This macro can be placed after a method declaration to allow the use of the C++11 feature "= delete" on all compilers. On newer compilers that support it, it does the C++11 "= delete", but on older ones it's just an empty definition. */ #define JUCE_DELETED_FUNCTION #endif #if ! DOXYGEN #if ! JUCE_COMPILER_SUPPORTS_NOEXCEPT #ifdef noexcept #undef noexcept #endif #define noexcept throw() #if defined (_MSC_VER) && _MSC_VER > 1600 #define _ALLOW_KEYWORD_MACROS 1 // (to stop VC2012 complaining) #endif #endif #if ! JUCE_COMPILER_SUPPORTS_NULLPTR #ifdef nullptr #undef nullptr #endif #define nullptr (0) #endif #if ! JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL #undef override #define override #endif #endif #endif // JUCE_COMPILERSUPPORT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h000066400000000000000000000310541320201440200306360ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_PLATFORMDEFS_H_INCLUDED #define JUCE_PLATFORMDEFS_H_INCLUDED //============================================================================== /* This file defines miscellaneous macros for debugging, assertions, etc. */ //============================================================================== #ifdef JUCE_FORCE_DEBUG #undef JUCE_DEBUG #if JUCE_FORCE_DEBUG #define JUCE_DEBUG 1 #endif #endif /** This macro defines the C calling convention used as the standard for Juce calls. */ #if JUCE_MSVC #define JUCE_CALLTYPE __stdcall #define JUCE_CDECL __cdecl #else #define JUCE_CALLTYPE #define JUCE_CDECL #endif //============================================================================== // Debugging and assertion macros #if JUCE_LOG_ASSERTIONS || JUCE_DEBUG #define juce_LogCurrentAssertion juce::logAssertion (__FILE__, __LINE__); #else #define juce_LogCurrentAssertion #endif //============================================================================== #if JUCE_IOS || JUCE_LINUX || JUCE_ANDROID || JUCE_PPC /** This will try to break into the debugger if the app is currently being debugged. If called by an app that's not being debugged, the behaviour isn't defined - it may crash or not, depending on the platform. @see jassert() */ #define juce_breakDebugger { ::kill (0, SIGTRAP); } #elif JUCE_USE_MSVC_INTRINSICS #ifndef __INTEL_COMPILER #pragma intrinsic (__debugbreak) #endif #define juce_breakDebugger { __debugbreak(); } #elif JUCE_GCC || JUCE_MAC #if JUCE_NO_INLINE_ASM #define juce_breakDebugger { } #else #define juce_breakDebugger { asm ("int $3"); } #endif #else #define juce_breakDebugger { __asm int 3 } #endif #if JUCE_CLANG && defined (__has_feature) && ! defined (JUCE_ANALYZER_NORETURN) #if __has_feature (attribute_analyzer_noreturn) inline void __attribute__((analyzer_noreturn)) juce_assert_noreturn() {} #define JUCE_ANALYZER_NORETURN juce_assert_noreturn(); #endif #endif #ifndef JUCE_ANALYZER_NORETURN #define JUCE_ANALYZER_NORETURN #endif //============================================================================== #if JUCE_MSVC && ! DOXYGEN #define MACRO_WITH_FORCED_SEMICOLON(x) \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ do { x } while (false) \ __pragma(warning(pop)) #else /** This is the good old C++ trick for creating a macro that forces the user to put a semicolon after it when they use it. */ #define MACRO_WITH_FORCED_SEMICOLON(x) do { x } while (false) #endif //============================================================================== #if JUCE_DEBUG || DOXYGEN /** Writes a string to the standard error stream. This is only compiled in a debug build. @see Logger::outputDebugString */ #define DBG(dbgtext) MACRO_WITH_FORCED_SEMICOLON (juce::String tempDbgBuf; tempDbgBuf << dbgtext; juce::Logger::outputDebugString (tempDbgBuf);) //============================================================================== /** This will always cause an assertion failure. It is only compiled in a debug build, (unless JUCE_LOG_ASSERTIONS is enabled for your build). @see jassert */ #define jassertfalse MACRO_WITH_FORCED_SEMICOLON (juce_LogCurrentAssertion; if (juce::juce_isRunningUnderDebugger()) juce_breakDebugger; JUCE_ANALYZER_NORETURN) //============================================================================== /** Platform-independent assertion macro. This macro gets turned into a no-op when you're building with debugging turned off, so be careful that the expression you pass to it doesn't perform any actions that are vital for the correct behaviour of your program! @see jassertfalse */ #define jassert(expression) MACRO_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else //============================================================================== // If debugging is disabled, these dummy debug and assertion macros are used.. #define DBG(dbgtext) #define jassertfalse MACRO_WITH_FORCED_SEMICOLON (juce_LogCurrentAssertion) #if JUCE_LOG_ASSERTIONS #define jassert(expression) MACRO_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else #define jassert(a) MACRO_WITH_FORCED_SEMICOLON ( ; ) #endif #endif //============================================================================== #ifndef DOXYGEN namespace juce { template struct JuceStaticAssert; template <> struct JuceStaticAssert { static void dummy() {} }; } #endif /** A compile-time assertion macro. If the expression parameter is false, the macro will cause a compile error. (The actual error message that the compiler generates may be completely bizarre and seem to have no relation to the place where you put the static_assert though!) */ #define static_jassert(expression) juce::JuceStaticAssert::dummy(); /** This is a shorthand macro for declaring stubs for a class's copy constructor and operator=. For example, instead of @code class MyClass { etc.. private: MyClass (const MyClass&); MyClass& operator= (const MyClass&); };@endcode ..you can just write: @code class MyClass { etc.. private: JUCE_DECLARE_NON_COPYABLE (MyClass) };@endcode */ #define JUCE_DECLARE_NON_COPYABLE(className) \ className (const className&) JUCE_DELETED_FUNCTION;\ className& operator= (const className&) JUCE_DELETED_FUNCTION; /** This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for a class. */ #define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className) \ JUCE_DECLARE_NON_COPYABLE(className) \ JUCE_LEAK_DETECTOR(className) /** This macro can be added to class definitions to disable the use of new/delete to allocate the object on the heap, forcing it to only be used as a stack or member variable. */ #define JUCE_PREVENT_HEAP_ALLOCATION \ private: \ static void* operator new (size_t) JUCE_DELETED_FUNCTION; \ static void operator delete (void*) JUCE_DELETED_FUNCTION; //============================================================================== #if ! DOXYGEN #define JUCE_JOIN_MACRO_HELPER(a, b) a ## b #define JUCE_STRINGIFY_MACRO_HELPER(a) #a #endif /** A good old-fashioned C macro concatenation helper. This combines two items (which may themselves be macros) into a single string, avoiding the pitfalls of the ## macro operator. */ #define JUCE_JOIN_MACRO(item1, item2) JUCE_JOIN_MACRO_HELPER (item1, item2) /** A handy C macro for stringifying any symbol, rather than just a macro parameter. */ #define JUCE_STRINGIFY(item) JUCE_STRINGIFY_MACRO_HELPER (item) //============================================================================== #if JUCE_MSVC && ! defined (DOXYGEN) #define JUCE_WARNING_HELPER(file, line, mess) message(file "(" JUCE_STRINGIFY (line) ") : Warning: " #mess) #define JUCE_COMPILER_WARNING(message) __pragma(JUCE_WARNING_HELPER (__FILE__, __LINE__, message)); #else #ifndef DOXYGEN #define JUCE_WARNING_HELPER(mess) message(#mess) #endif /** This macro allows you to emit a custom compiler warning message. Very handy for marking bits of code as "to-do" items, or for shaming code written by your co-workers in a way that's hard to ignore. GCC and Clang provide the \#warning directive, but MSVC doesn't, so this macro is a cross-compiler way to get the same functionality as \#warning. */ #define JUCE_COMPILER_WARNING(message) _Pragma(JUCE_STRINGIFY (JUCE_WARNING_HELPER (message))); #endif //============================================================================== #if JUCE_CATCH_UNHANDLED_EXCEPTIONS #define JUCE_TRY try #define JUCE_CATCH_ALL catch (...) {} #define JUCE_CATCH_ALL_ASSERT catch (...) { jassertfalse; } #if ! JUCE_MODULE_AVAILABLE_juce_gui_basics #define JUCE_CATCH_EXCEPTION JUCE_CATCH_ALL #else /** Used in try-catch blocks, this macro will send exceptions to the JUCEApplicationBase object so they can be logged by the application if it wants to. */ #define JUCE_CATCH_EXCEPTION \ catch (const std::exception& e) \ { \ juce::JUCEApplicationBase::sendUnhandledException (&e, __FILE__, __LINE__); \ } \ catch (...) \ { \ juce::JUCEApplicationBase::sendUnhandledException (nullptr, __FILE__, __LINE__); \ } #endif #else #define JUCE_TRY #define JUCE_CATCH_EXCEPTION #define JUCE_CATCH_ALL #define JUCE_CATCH_ALL_ASSERT #endif //============================================================================== #if JUCE_DEBUG || DOXYGEN /** A platform-independent way of forcing an inline function. Use the syntax: @code forcedinline void myfunction (int x) @endcode */ #define forcedinline inline #else #if JUCE_MSVC #define forcedinline __forceinline #else #define forcedinline inline __attribute__((always_inline)) #endif #endif #if JUCE_MSVC || DOXYGEN /** This can be placed before a stack or member variable declaration to tell the compiler to align it to the specified number of bytes. */ #define JUCE_ALIGN(bytes) __declspec (align (bytes)) #else #define JUCE_ALIGN(bytes) __attribute__ ((aligned (bytes))) #endif //============================================================================== // Cross-compiler deprecation macros.. #ifdef DOXYGEN /** This macro can be used to wrap a function which has been deprecated. */ #define JUCE_DEPRECATED(functionDef) #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) #elif JUCE_MSVC && ! JUCE_NO_DEPRECATION_WARNINGS #define JUCE_DEPRECATED(functionDef) __declspec(deprecated) functionDef #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) __declspec(deprecated) functionDef body #elif JUCE_GCC && ! JUCE_NO_DEPRECATION_WARNINGS #define JUCE_DEPRECATED(functionDef) functionDef __attribute__ ((deprecated)) #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef __attribute__ ((deprecated)) body #else #define JUCE_DEPRECATED(functionDef) functionDef #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef body #endif //============================================================================== #if JUCE_ANDROID && ! DOXYGEN #define JUCE_MODAL_LOOPS_PERMITTED 0 #elif ! defined (JUCE_MODAL_LOOPS_PERMITTED) /** Some operating environments don't provide a modal loop mechanism, so this flag can be used to disable any functions that try to run a modal loop. */ #define JUCE_MODAL_LOOPS_PERMITTED 1 #endif //============================================================================== #if JUCE_GCC #define JUCE_PACKED __attribute__((packed)) #elif ! DOXYGEN #define JUCE_PACKED #endif #endif // JUCE_PLATFORMDEFS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h000066400000000000000000000117031320201440200311200ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_STANDARDHEADER_H_INCLUDED #define JUCE_STANDARDHEADER_H_INCLUDED //============================================================================== /** Current JUCE version number. See also SystemStats::getJUCEVersion() for a string version. */ #define JUCE_MAJOR_VERSION 3 #define JUCE_MINOR_VERSION 2 #define JUCE_BUILDNUMBER 0 /** Current Juce version number. Bits 16 to 32 = major version. Bits 8 to 16 = minor version. Bits 0 to 8 = point release. See also SystemStats::getJUCEVersion() for a string version. */ #define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) //============================================================================== #include // included before platform defs to provide a definition of _LIBCPP_VERSION #include "juce_PlatformDefs.h" #include "juce_CompilerSupport.h" //============================================================================== // Now we'll include some common OS headers.. #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4245 4100) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #if JUCE_USE_MSVC_INTRINSICS #include #endif #if JUCE_MAC || JUCE_IOS #include #endif #if JUCE_LINUX #include #if __INTEL_COMPILER #if __ia64__ #include #else #include #endif #endif #endif #if JUCE_MSVC && JUCE_DEBUG #include #endif #if JUCE_MSVC #pragma warning (pop) #endif #if JUCE_ANDROID #include #include #endif // undef symbols that are sometimes set by misguided 3rd-party headers.. #undef check #undef TYPE_BOOL #undef max #undef min #undef major #undef minor //============================================================================== // DLL building settings on Windows #if JUCE_MSVC #ifdef JUCE_DLL_BUILD #define JUCE_API __declspec (dllexport) #pragma warning (disable: 4251) #elif defined (JUCE_DLL) #define JUCE_API __declspec (dllimport) #pragma warning (disable: 4251) #endif #ifdef __INTEL_COMPILER #pragma warning (disable: 1125) // (virtual override warning) #endif #elif defined (JUCE_DLL) || defined (JUCE_DLL_BUILD) #define JUCE_API __attribute__ ((visibility("default"))) #endif //============================================================================== #ifndef JUCE_API #define JUCE_API /**< This macro is added to all juce public class declarations. */ #endif #if JUCE_MSVC && JUCE_DLL_BUILD #define JUCE_PUBLIC_IN_DLL_BUILD(declaration) public: declaration; private: #else #define JUCE_PUBLIC_IN_DLL_BUILD(declaration) declaration; #endif /** This macro is added to all juce public function declarations. */ #define JUCE_PUBLIC_FUNCTION JUCE_API JUCE_CALLTYPE #if (! defined (JUCE_CATCH_DEPRECATED_CODE_MISUSE)) && JUCE_DEBUG && ! DOXYGEN /** This turns on some non-essential bits of code that should prevent old code from compiling in cases where method signatures have changed, etc. */ #define JUCE_CATCH_DEPRECATED_CODE_MISUSE 1 #endif #ifndef DOXYGEN #define JUCE_NAMESPACE juce // This old macro is deprecated: you should just use the juce namespace directly. #endif #endif // JUCE_STANDARDHEADER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp000066400000000000000000000144251320201440200311110ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ String SystemStats::getJUCEVersion() { // Some basic tests, to keep an eye on things and make sure these types work ok // on all platforms. Let me know if any of these assertions fail on your system! static_jassert (sizeof (pointer_sized_int) == sizeof (void*)); static_jassert (sizeof (int8) == 1); static_jassert (sizeof (uint8) == 1); static_jassert (sizeof (int16) == 2); static_jassert (sizeof (uint16) == 2); static_jassert (sizeof (int32) == 4); static_jassert (sizeof (uint32) == 4); static_jassert (sizeof (int64) == 8); static_jassert (sizeof (uint64) == 8); return "JUCE v" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) "." JUCE_STRINGIFY(JUCE_MINOR_VERSION) "." JUCE_STRINGIFY(JUCE_BUILDNUMBER); } #if JUCE_ANDROID && ! defined (JUCE_DISABLE_JUCE_VERSION_PRINTING) #define JUCE_DISABLE_JUCE_VERSION_PRINTING 1 #endif #if JUCE_DEBUG && ! JUCE_DISABLE_JUCE_VERSION_PRINTING struct JuceVersionPrinter { JuceVersionPrinter() { DBG (SystemStats::getJUCEVersion()); } }; static JuceVersionPrinter juceVersionPrinter; #endif //============================================================================== struct CPUInformation { CPUInformation() noexcept : numCpus (0), hasMMX (false), hasSSE (false), hasSSE2 (false), hasSSE3 (false), has3DNow (false), hasSSSE3 (false), hasAVX (false) { initialise(); } void initialise() noexcept; int numCpus; bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow, hasSSSE3, hasAVX; }; static const CPUInformation& getCPUInformation() noexcept { static CPUInformation info; return info; } int SystemStats::getNumCpus() noexcept { return getCPUInformation().numCpus; } bool SystemStats::hasMMX() noexcept { return getCPUInformation().hasMMX; } bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSSE; } bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; } bool SystemStats::hasSSE3() noexcept { return getCPUInformation().hasSSE3; } bool SystemStats::hasSSSE3() noexcept { return getCPUInformation().hasSSSE3; } bool SystemStats::hasAVX() noexcept { return getCPUInformation().hasAVX; } //============================================================================== String SystemStats::getStackBacktrace() { String result; #if JUCE_ANDROID || JUCE_MINGW jassertfalse; // sorry, not implemented yet! #elif JUCE_WINDOWS HANDLE process = GetCurrentProcess(); SymInitialize (process, nullptr, TRUE); void* stack[128]; int frames = (int) CaptureStackBackTrace (0, numElementsInArray (stack), stack, nullptr); HeapBlock symbol; symbol.calloc (sizeof (SYMBOL_INFO) + 256, 1); symbol->MaxNameLen = 255; symbol->SizeOfStruct = sizeof (SYMBOL_INFO); for (int i = 0; i < frames; ++i) { DWORD64 displacement = 0; if (SymFromAddr (process, (DWORD64) stack[i], &displacement, symbol)) { result << i << ": "; IMAGEHLP_MODULE64 moduleInfo; zerostruct (moduleInfo); moduleInfo.SizeOfStruct = sizeof (moduleInfo); if (::SymGetModuleInfo64 (process, symbol->ModBase, &moduleInfo)) result << moduleInfo.ModuleName << ": "; result << symbol->Name << " + 0x" << String::toHexString ((int64) displacement) << newLine; } } #else void* stack[128]; int frames = backtrace (stack, numElementsInArray (stack)); char** frameStrings = backtrace_symbols (stack, frames); for (int i = 0; i < frames; ++i) result << frameStrings[i] << newLine; ::free (frameStrings); #endif return result; } //============================================================================== static SystemStats::CrashHandlerFunction globalCrashHandler = nullptr; #if JUCE_WINDOWS static LONG WINAPI handleCrash (LPEXCEPTION_POINTERS) { globalCrashHandler(); return EXCEPTION_EXECUTE_HANDLER; } #else static void handleCrash (int) { globalCrashHandler(); kill (getpid(), SIGKILL); } int juce_siginterrupt (int sig, int flag); #endif void SystemStats::setApplicationCrashHandler (CrashHandlerFunction handler) { jassert (handler != nullptr); // This must be a valid function. globalCrashHandler = handler; #if JUCE_WINDOWS SetUnhandledExceptionFilter (handleCrash); #else const int signals[] = { SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGABRT, SIGSYS }; for (int i = 0; i < numElementsInArray (signals); ++i) { ::signal (signals[i], handleCrash); juce_siginterrupt (signals[i], 1); } #endif } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h000066400000000000000000000202561320201440200305550ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SYSTEMSTATS_H_INCLUDED #define JUCE_SYSTEMSTATS_H_INCLUDED //============================================================================== /** Contains methods for finding out about the current hardware and OS configuration. */ class JUCE_API SystemStats { public: //============================================================================== /** Returns the current version of JUCE, See also the JUCE_VERSION, JUCE_MAJOR_VERSION and JUCE_MINOR_VERSION macros. */ static String getJUCEVersion(); //============================================================================== /** The set of possible results of the getOperatingSystemType() method. */ enum OperatingSystemType { UnknownOS = 0, MacOSX = 0x0100, /**< To test whether any version of OSX is running, you can use the expression ((getOperatingSystemType() & MacOSX) != 0). */ Windows = 0x0200, /**< To test whether any version of Windows is running, you can use the expression ((getOperatingSystemType() & Windows) != 0). */ Linux = 0x0400, Android = 0x0800, iOS = 0x1000, MacOSX_10_4 = MacOSX | 4, MacOSX_10_5 = MacOSX | 5, MacOSX_10_6 = MacOSX | 6, MacOSX_10_7 = MacOSX | 7, MacOSX_10_8 = MacOSX | 8, MacOSX_10_9 = MacOSX | 9, MacOSX_10_10 = MacOSX | 10, Win2000 = Windows | 1, WinXP = Windows | 2, WinVista = Windows | 3, Windows7 = Windows | 4, Windows8_0 = Windows | 5, Windows8_1 = Windows | 6, Windows10 = Windows | 7 }; /** Returns the type of operating system we're running on. @returns one of the values from the OperatingSystemType enum. @see getOperatingSystemName */ static OperatingSystemType getOperatingSystemType(); /** Returns the name of the type of operating system we're running on. @returns a string describing the OS type. @see getOperatingSystemType */ static String getOperatingSystemName(); /** Returns true if the OS is 64-bit, or false for a 32-bit OS. */ static bool isOperatingSystem64Bit(); /** Returns an environment variable. If the named value isn't set, this will return the defaultValue string instead. */ static String getEnvironmentVariable (const String& name, const String& defaultValue); //============================================================================== /** Returns the current user's name, if available. @see getFullUserName() */ static String getLogonName(); /** Returns the current user's full name, if available. On some OSes, this may just return the same value as getLogonName(). @see getLogonName() */ static String getFullUserName(); /** Returns the host-name of the computer. */ static String getComputerName(); /** Returns the language of the user's locale. The return value is a 2 or 3 letter language code (ISO 639-1 or ISO 639-2) */ static String getUserLanguage(); /** Returns the region of the user's locale. The return value is a 2 letter country code (ISO 3166-1 alpha-2). */ static String getUserRegion(); /** Returns the user's display language. The return value is a 2 or 3 letter language code (ISO 639-1 or ISO 639-2). Note that depending on the OS and region, this may also be followed by a dash and a sub-region code, e.g "en-GB" */ static String getDisplayLanguage(); /** This will attempt to return some kind of string describing the device. If no description is available, it'll just return an empty string. You may want to use this for things like determining the type of phone/iPad, etc. */ static String getDeviceDescription(); //============================================================================== // CPU and memory information.. /** Returns the number of CPU cores. */ static int getNumCpus() noexcept; /** Returns the approximate CPU speed. @returns the speed in megahertz, e.g. 1500, 2500, 32000 (depending on what year you're reading this...) */ static int getCpuSpeedInMegaherz(); /** Returns a string to indicate the CPU vendor. Might not be known on some systems. */ static String getCpuVendor(); static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */ static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */ static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */ static bool hasSSE2() noexcept; /**< Returns true if Intel SSE2 instructions are available. */ static bool hasSSE3() noexcept; /**< Returns true if Intel SSE2 instructions are available. */ static bool hasSSSE3() noexcept; /**< Returns true if Intel SSSE3 instructions are available. */ static bool hasAVX() noexcept; /**< Returns true if Intel AVX instructions are available. */ //============================================================================== /** Finds out how much RAM is in the machine. @returns the approximate number of megabytes of memory, or zero if something goes wrong when finding out. */ static int getMemorySizeInMegabytes(); /** Returns the system page-size. This is only used by programmers with beards. */ static int getPageSize(); //============================================================================== /** Returns a backtrace of the current call-stack. The usefulness of the result will depend on the level of debug symbols that are available in the executable. */ static String getStackBacktrace(); /** A void() function type, used by setApplicationCrashHandler(). */ typedef void (*CrashHandlerFunction)(); /** Sets up a global callback function that will be called if the application executes some kind of illegal instruction. You may want to call getStackBacktrace() in your handler function, to find out where the problem happened and log it, etc. */ static void setApplicationCrashHandler (CrashHandlerFunction); private: //============================================================================== SystemStats(); JUCE_DECLARE_NON_COPYABLE (SystemStats) }; #endif // JUCE_SYSTEMSTATS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h000066400000000000000000000141101320201440200311750ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_TARGETPLATFORM_H_INCLUDED #define JUCE_TARGETPLATFORM_H_INCLUDED //============================================================================== /* This file figures out which platform is being built, and defines some macros that the rest of the code can use for OS-specific compilation. Macros that will be set here are: - One of JUCE_WINDOWS, JUCE_MAC JUCE_LINUX, JUCE_IOS, JUCE_ANDROID, etc. - Either JUCE_32BIT or JUCE_64BIT, depending on the architecture. - Either JUCE_LITTLE_ENDIAN or JUCE_BIG_ENDIAN. - Either JUCE_INTEL or JUCE_PPC - Either JUCE_GCC or JUCE_MSVC */ //============================================================================== #if (defined (_WIN32) || defined (_WIN64)) #define JUCE_WIN32 1 #define JUCE_WINDOWS 1 #elif defined (JUCE_ANDROID) #undef JUCE_ANDROID #define JUCE_ANDROID 1 #elif defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 #elif defined (__APPLE_CPP__) || defined(__APPLE_CC__) #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #define Component CarbonDummyCompName #include // (needed to find out what platform we're using) #undef Point #undef Component #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR #define JUCE_IPHONE 1 #define JUCE_IOS 1 #else #define JUCE_MAC 1 #endif #elif defined (__FreeBSD__) #define JUCE_BSD 1 #else #error "Unknown platform!" #endif //============================================================================== #if JUCE_WINDOWS #ifdef _MSC_VER #ifdef _WIN64 #define JUCE_64BIT 1 #else #define JUCE_32BIT 1 #endif #endif #ifdef _DEBUG #define JUCE_DEBUG 1 #endif #ifdef __MINGW32__ #define JUCE_MINGW 1 #ifdef __MINGW64__ #define JUCE_64BIT 1 #else #define JUCE_32BIT 1 #endif #endif /** If defined, this indicates that the processor is little-endian. */ #define JUCE_LITTLE_ENDIAN 1 #define JUCE_INTEL 1 #endif //============================================================================== #if JUCE_MAC || JUCE_IOS #if defined (DEBUG) || defined (_DEBUG) || ! (defined (NDEBUG) || defined (_NDEBUG)) #define JUCE_DEBUG 1 #endif #if ! (defined (DEBUG) || defined (_DEBUG) || defined (NDEBUG) || defined (_NDEBUG)) #warning "Neither NDEBUG or DEBUG has been defined - you should set one of these to make it clear whether this is a release build," #endif #ifdef __LITTLE_ENDIAN__ #define JUCE_LITTLE_ENDIAN 1 #else #define JUCE_BIG_ENDIAN 1 #endif #ifdef __LP64__ #define JUCE_64BIT 1 #else #define JUCE_32BIT 1 #endif #if defined (__ppc__) || defined (__ppc64__) #define JUCE_PPC 1 #elif defined (__arm__) || defined (__arm64__) #define JUCE_ARM 1 #else #define JUCE_INTEL 1 #endif #if JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 #error "Building for OSX 10.3 is no longer supported!" #endif #if JUCE_MAC && ! defined (MAC_OS_X_VERSION_10_5) #error "To build with 10.4 compatibility, use a 10.5 or 10.6 SDK and set the deployment target to 10.4" #endif #endif //============================================================================== #if JUCE_LINUX || JUCE_ANDROID #ifdef _DEBUG #define JUCE_DEBUG 1 #endif // Allow override for big-endian Linux platforms #if defined (__LITTLE_ENDIAN__) || ! defined (JUCE_BIG_ENDIAN) #define JUCE_LITTLE_ENDIAN 1 #undef JUCE_BIG_ENDIAN #else #undef JUCE_LITTLE_ENDIAN #define JUCE_BIG_ENDIAN 1 #endif #if defined (__LP64__) || defined (_LP64) || defined (__arm64__) #define JUCE_64BIT 1 #else #define JUCE_32BIT 1 #endif #if defined (__arm__) || defined (__arm64__) #define JUCE_ARM 1 #elif __MMX__ || __SSE__ || __amd64__ #define JUCE_INTEL 1 #endif #endif //============================================================================== // Compiler type macros. #ifdef __clang__ #define JUCE_CLANG 1 #define JUCE_GCC 1 #elif defined (__GNUC__) #define JUCE_GCC 1 #elif defined (_MSC_VER) #define JUCE_MSVC 1 #if _MSC_VER < 1500 #define JUCE_VC8_OR_EARLIER 1 #if _MSC_VER < 1400 #define JUCE_VC7_OR_EARLIER 1 #if _MSC_VER < 1300 #warning "MSVC 6.0 is no longer supported!" #endif #endif #endif #if JUCE_64BIT || ! JUCE_VC7_OR_EARLIER #define JUCE_USE_MSVC_INTRINSICS 1 #endif #else #error unknown compiler #endif #endif // JUCE_TARGETPLATFORM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/000077500000000000000000000000001320201440200245265ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h000066400000000000000000000355161320201440200310450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CHARPOINTER_ASCII_H_INCLUDED #define JUCE_CHARPOINTER_ASCII_H_INCLUDED //============================================================================== /** Wraps a pointer to a null-terminated ASCII character string, and provides various methods to operate on the data. A valid ASCII string is assumed to not contain any characters above 127. @see CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32 */ class CharPointer_ASCII { public: typedef char CharType; inline explicit CharPointer_ASCII (const CharType* const rawPointer) noexcept : data (const_cast (rawPointer)) { } inline CharPointer_ASCII (const CharPointer_ASCII& other) noexcept : data (other.data) { } inline CharPointer_ASCII operator= (const CharPointer_ASCII other) noexcept { data = other.data; return *this; } inline CharPointer_ASCII operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ inline bool operator== (CharPointer_ASCII other) const noexcept { return data == other.data; } inline bool operator!= (CharPointer_ASCII other) const noexcept { return data != other.data; } inline bool operator<= (CharPointer_ASCII other) const noexcept { return data <= other.data; } inline bool operator< (CharPointer_ASCII other) const noexcept { return data < other.data; } inline bool operator>= (CharPointer_ASCII other) const noexcept { return data >= other.data; } inline bool operator> (CharPointer_ASCII other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } /** Returns the address that this pointer is pointing to. */ inline operator const CharType*() const noexcept { return data; } /** Returns true if this pointer is pointing to a null character. */ inline bool isEmpty() const noexcept { return *data == 0; } /** Returns the unicode character that this pointer is pointing to. */ inline juce_wchar operator*() const noexcept { return (juce_wchar) (uint8) *data; } /** Moves this pointer along to the next character in the string. */ inline CharPointer_ASCII operator++() noexcept { ++data; return *this; } /** Moves this pointer to the previous character in the string. */ inline CharPointer_ASCII operator--() noexcept { --data; return *this; } /** Returns the character that this pointer is currently pointing to, and then advances the pointer to point to the next character. */ inline juce_wchar getAndAdvance() noexcept { return (juce_wchar) (uint8) *data++; } /** Moves this pointer along to the next character in the string. */ CharPointer_ASCII operator++ (int) noexcept { CharPointer_ASCII temp (*this); ++data; return temp; } /** Moves this pointer forwards by the specified number of characters. */ inline void operator+= (const int numToSkip) noexcept { data += numToSkip; } inline void operator-= (const int numToSkip) noexcept { data -= numToSkip; } /** Returns the character at a given character index from the start of the string. */ inline juce_wchar operator[] (const int characterIndex) const noexcept { return (juce_wchar) (unsigned char) data [characterIndex]; } /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ CharPointer_ASCII operator+ (const int numToSkip) const noexcept { return CharPointer_ASCII (data + numToSkip); } /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ CharPointer_ASCII operator- (const int numToSkip) const noexcept { return CharPointer_ASCII (data - numToSkip); } /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ inline void write (const juce_wchar charToWrite) noexcept { *data++ = (char) charToWrite; } inline void replaceChar (const juce_wchar newChar) noexcept { *data = (char) newChar; } /** Writes a null character to this string (leaving the pointer's position unchanged). */ inline void writeNull() const noexcept { *data = 0; } /** Returns the number of characters in this string. */ size_t length() const noexcept { return (size_t) strlen (data); } /** Returns the number of characters in this string, or the given value, whichever is lower. */ size_t lengthUpTo (const size_t maxCharsToCount) const noexcept { return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ size_t lengthUpTo (const CharPointer_ASCII end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } /** Returns the number of bytes that are used to represent this string. This includes the terminating null character. */ size_t sizeInBytes() const noexcept { return length() + 1; } /** Returns the number of bytes that would be needed to represent the given unicode character in this encoding format. */ static inline size_t getBytesRequiredFor (const juce_wchar) noexcept { return 1; } /** Returns the number of bytes that would be needed to represent the given string in this encoding format. The value returned does NOT include the terminating null character. */ template static size_t getBytesRequiredFor (const CharPointer text) noexcept { return text.length(); } /** Returns a pointer to the null character that terminates this string. */ CharPointer_ASCII findTerminatingNull() const noexcept { return CharPointer_ASCII (data + length()); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ template void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ void writeAll (const CharPointer_ASCII src) noexcept { strcpy (data, src.data); } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxDestBytes parameter specifies the maximum number of bytes that can be written to the destination buffer before stopping. */ template size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxChars parameter specifies the maximum number of characters that can be written to the destination buffer before stopping (including the terminating null). */ template void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } /** Compares this string with another one. */ int compare (const CharPointer_ASCII other) const noexcept { return strcmp (data, other.data); } /** Compares this string with another one, up to a specified number of characters. */ template int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one, up to a specified number of characters. */ int compareUpTo (const CharPointer_ASCII other, const int maxChars) const noexcept { return strncmp (data, other.data, (size_t) maxChars); } /** Compares this string with another one. */ template int compareIgnoreCase (const CharPointer other) const { return CharacterFunctions::compareIgnoreCase (*this, other); } int compareIgnoreCase (const CharPointer_ASCII other) const { #if JUCE_MSVC return stricmp (data, other.data); #elif JUCE_MINGW return CharacterFunctions::compareIgnoreCase (*this, other); #else return strcasecmp (data, other.data); #endif } /** Compares this string with another one, up to a specified number of characters. */ template int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } /** Returns the character index of a substring, or -1 if it isn't found. */ template int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind) const noexcept { int i = 0; while (data[i] != 0) { if (data[i] == (char) charToFind) return i; ++i; } return -1; } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind, const bool ignoreCase) const noexcept { return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) : CharacterFunctions::indexOfChar (*this, charToFind); } /** Returns true if the first character of this string is whitespace. */ bool isWhitespace() const { return CharacterFunctions::isWhitespace (*data) != 0; } /** Returns true if the first character of this string is a digit. */ bool isDigit() const { return CharacterFunctions::isDigit (*data) != 0; } /** Returns true if the first character of this string is a letter. */ bool isLetter() const { return CharacterFunctions::isLetter (*data) != 0; } /** Returns true if the first character of this string is a letter or digit. */ bool isLetterOrDigit() const { return CharacterFunctions::isLetterOrDigit (*data) != 0; } /** Returns true if the first character of this string is upper-case. */ bool isUpperCase() const { return CharacterFunctions::isUpperCase ((juce_wchar) (uint8) *data) != 0; } /** Returns true if the first character of this string is lower-case. */ bool isLowerCase() const { return CharacterFunctions::isLowerCase ((juce_wchar) (uint8) *data) != 0; } /** Returns an upper-case version of the first character of this string. */ juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase ((juce_wchar) (uint8) *data); } /** Returns a lower-case version of the first character of this string. */ juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase ((juce_wchar) (uint8) *data); } /** Parses this string as a 32-bit integer. */ int getIntValue32() const noexcept { return atoi (data); } /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { #if JUCE_LINUX || JUCE_ANDROID || JUCE_MINGW return atoll (data); #elif JUCE_WINDOWS return _atoi64 (data); #else return CharacterFunctions::getIntValue (*this); #endif } /** Parses this string as a floating point double. */ double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); } /** Returns the first non-whitespace character in the string. */ CharPointer_ASCII findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { return ((unsigned int) character) < (unsigned int) 128; } /** Returns true if this data contains a valid string in this encoding. */ static bool isValidString (const CharType* dataToTest, int maxBytesToRead) { while (--maxBytesToRead >= 0) { if (((signed char) *dataToTest) <= 0) return *dataToTest == 0; ++dataToTest; } return true; } private: CharType* data; }; #endif // JUCE_CHARPOINTER_ASCII_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h000066400000000000000000000452511320201440200310170ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CHARPOINTER_UTF16_H_INCLUDED #define JUCE_CHARPOINTER_UTF16_H_INCLUDED //============================================================================== /** Wraps a pointer to a null-terminated UTF-16 character string, and provides various methods to operate on the data. @see CharPointer_UTF8, CharPointer_UTF32 */ class CharPointer_UTF16 { public: #if JUCE_NATIVE_WCHAR_IS_UTF16 typedef wchar_t CharType; #else typedef int16 CharType; #endif inline explicit CharPointer_UTF16 (const CharType* const rawPointer) noexcept : data (const_cast (rawPointer)) { } inline CharPointer_UTF16 (const CharPointer_UTF16& other) noexcept : data (other.data) { } inline CharPointer_UTF16 operator= (CharPointer_UTF16 other) noexcept { data = other.data; return *this; } inline CharPointer_UTF16 operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ inline bool operator== (CharPointer_UTF16 other) const noexcept { return data == other.data; } inline bool operator!= (CharPointer_UTF16 other) const noexcept { return data != other.data; } inline bool operator<= (CharPointer_UTF16 other) const noexcept { return data <= other.data; } inline bool operator< (CharPointer_UTF16 other) const noexcept { return data < other.data; } inline bool operator>= (CharPointer_UTF16 other) const noexcept { return data >= other.data; } inline bool operator> (CharPointer_UTF16 other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } /** Returns the address that this pointer is pointing to. */ inline operator const CharType*() const noexcept { return data; } /** Returns true if this pointer is pointing to a null character. */ inline bool isEmpty() const noexcept { return *data == 0; } /** Returns the unicode character that this pointer is pointing to. */ juce_wchar operator*() const noexcept { uint32 n = (uint32) (uint16) *data; if (n >= 0xd800 && n <= 0xdfff && ((uint32) (uint16) data[1]) >= 0xdc00) n = 0x10000 + (((n - 0xd800) << 10) | (((uint32) (uint16) data[1]) - 0xdc00)); return (juce_wchar) n; } /** Moves this pointer along to the next character in the string. */ CharPointer_UTF16 operator++() noexcept { const juce_wchar n = *data++; if (n >= 0xd800 && n <= 0xdfff && ((uint32) (uint16) *data) >= 0xdc00) ++data; return *this; } /** Moves this pointer back to the previous character in the string. */ CharPointer_UTF16 operator--() noexcept { const juce_wchar n = *--data; if (n >= 0xdc00 && n <= 0xdfff) --data; return *this; } /** Returns the character that this pointer is currently pointing to, and then advances the pointer to point to the next character. */ juce_wchar getAndAdvance() noexcept { uint32 n = (uint32) (uint16) *data++; if (n >= 0xd800 && n <= 0xdfff && ((uint32) (uint16) *data) >= 0xdc00) n = 0x10000 + ((((n - 0xd800) << 10) | (((uint32) (uint16) *data++) - 0xdc00))); return (juce_wchar) n; } /** Moves this pointer along to the next character in the string. */ CharPointer_UTF16 operator++ (int) noexcept { CharPointer_UTF16 temp (*this); ++*this; return temp; } /** Moves this pointer forwards by the specified number of characters. */ void operator+= (int numToSkip) noexcept { if (numToSkip < 0) { while (++numToSkip <= 0) --*this; } else { while (--numToSkip >= 0) ++*this; } } /** Moves this pointer backwards by the specified number of characters. */ void operator-= (int numToSkip) noexcept { operator+= (-numToSkip); } /** Returns the character at a given character index from the start of the string. */ juce_wchar operator[] (const int characterIndex) const noexcept { CharPointer_UTF16 p (*this); p += characterIndex; return *p; } /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ CharPointer_UTF16 operator+ (const int numToSkip) const noexcept { CharPointer_UTF16 p (*this); p += numToSkip; return p; } /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ CharPointer_UTF16 operator- (const int numToSkip) const noexcept { CharPointer_UTF16 p (*this); p += -numToSkip; return p; } /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ void write (juce_wchar charToWrite) noexcept { if (charToWrite >= 0x10000) { charToWrite -= 0x10000; *data++ = (CharType) (0xd800 + (charToWrite >> 10)); *data++ = (CharType) (0xdc00 + (charToWrite & 0x3ff)); } else { *data++ = (CharType) charToWrite; } } /** Writes a null character to this string (leaving the pointer's position unchanged). */ inline void writeNull() const noexcept { *data = 0; } /** Returns the number of characters in this string. */ size_t length() const noexcept { const CharType* d = data; size_t count = 0; for (;;) { const int n = *d++; if (n >= 0xd800 && n <= 0xdfff) { if (*d++ == 0) break; } else if (n == 0) break; ++count; } return count; } /** Returns the number of characters in this string, or the given value, whichever is lower. */ size_t lengthUpTo (const size_t maxCharsToCount) const noexcept { return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ size_t lengthUpTo (const CharPointer_UTF16 end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } /** Returns the number of bytes that are used to represent this string. This includes the terminating null character. */ size_t sizeInBytes() const noexcept { return sizeof (CharType) * (findNullIndex (data) + 1); } /** Returns the number of bytes that would be needed to represent the given unicode character in this encoding format. */ static size_t getBytesRequiredFor (const juce_wchar charToWrite) noexcept { return (charToWrite >= 0x10000) ? (sizeof (CharType) * 2) : sizeof (CharType); } /** Returns the number of bytes that would be needed to represent the given string in this encoding format. The value returned does NOT include the terminating null character. */ template static size_t getBytesRequiredFor (CharPointer text) noexcept { size_t count = 0; juce_wchar n; while ((n = text.getAndAdvance()) != 0) count += getBytesRequiredFor (n); return count; } /** Returns a pointer to the null character that terminates this string. */ CharPointer_UTF16 findTerminatingNull() const noexcept { const CharType* t = data; while (*t != 0) ++t; return CharPointer_UTF16 (t); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ template void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ void writeAll (const CharPointer_UTF16 src) noexcept { const CharType* s = src.data; while ((*data = *s) != 0) { ++data; ++s; } } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxDestBytes parameter specifies the maximum number of bytes that can be written to the destination buffer before stopping. */ template size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxChars parameter specifies the maximum number of characters that can be written to the destination buffer before stopping (including the terminating null). */ template void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one. */ template int compareIgnoreCase (const CharPointer other) const noexcept { return CharacterFunctions::compareIgnoreCase (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } #if JUCE_MSVC && ! DOXYGEN int compareIgnoreCase (const CharPointer_UTF16 other) const noexcept { return _wcsicmp (data, other.data); } int compareIgnoreCaseUpTo (const CharPointer_UTF16 other, int maxChars) const noexcept { return _wcsnicmp (data, other.data, (size_t) maxChars); } int indexOf (const CharPointer_UTF16 stringToFind) const noexcept { const CharType* const t = wcsstr (data, stringToFind.getAddress()); return t == nullptr ? -1 : (int) (t - data); } #endif /** Returns the character index of a substring, or -1 if it isn't found. */ template int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind) const noexcept { return CharacterFunctions::indexOfChar (*this, charToFind); } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind, const bool ignoreCase) const noexcept { return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) : CharacterFunctions::indexOfChar (*this, charToFind); } /** Returns true if the first character of this string is whitespace. */ bool isWhitespace() const noexcept { return CharacterFunctions::isWhitespace (operator*()) != 0; } /** Returns true if the first character of this string is a digit. */ bool isDigit() const noexcept { return CharacterFunctions::isDigit (operator*()) != 0; } /** Returns true if the first character of this string is a letter. */ bool isLetter() const noexcept { return CharacterFunctions::isLetter (operator*()) != 0; } /** Returns true if the first character of this string is a letter or digit. */ bool isLetterOrDigit() const noexcept { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; } /** Returns true if the first character of this string is upper-case. */ bool isUpperCase() const noexcept { return CharacterFunctions::isUpperCase (operator*()) != 0; } /** Returns true if the first character of this string is lower-case. */ bool isLowerCase() const noexcept { return CharacterFunctions::isLowerCase (operator*()) != 0; } /** Returns an upper-case version of the first character of this string. */ juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase (operator*()); } /** Returns a lower-case version of the first character of this string. */ juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase (operator*()); } /** Parses this string as a 32-bit integer. */ int getIntValue32() const noexcept { #if JUCE_MSVC return _wtoi (data); #else return CharacterFunctions::getIntValue (*this); #endif } /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { #if JUCE_MSVC return _wtoi64 (data); #else return CharacterFunctions::getIntValue (*this); #endif } /** Parses this string as a floating point double. */ double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); } /** Returns the first non-whitespace character in the string. */ CharPointer_UTF16 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { return ((unsigned int) character) < (unsigned int) 0x10ffff && (((unsigned int) character) < 0xd800 || ((unsigned int) character) > 0xdfff); } /** Returns true if this data contains a valid string in this encoding. */ static bool isValidString (const CharType* dataToTest, int maxBytesToRead) { maxBytesToRead /= (int) sizeof (CharType); while (--maxBytesToRead >= 0 && *dataToTest != 0) { const uint32 n = (uint32) (uint16) *dataToTest++; if (n >= 0xd800) { if (n > 0x10ffff) return false; if (n <= 0xdfff) { if (n > 0xdc00) return false; const uint32 nextChar = (uint32) (uint16) *dataToTest++; if (nextChar < 0xdc00 || nextChar > 0xdfff) return false; } } } return true; } /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF16 atomicSwap (const CharPointer_UTF16 newValue) { return CharPointer_UTF16 (reinterpret_cast &> (data).exchange (newValue.data)); } /** These values are the byte-order-mark (BOM) values for a UTF-16 stream. */ enum { byteOrderMarkBE1 = 0xfe, byteOrderMarkBE2 = 0xff, byteOrderMarkLE1 = 0xff, byteOrderMarkLE2 = 0xfe }; /** Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (big endian). The pointer must not be null, and must contain at least two valid bytes. */ static bool isByteOrderMarkBigEndian (const void* possibleByteOrder) noexcept { jassert (possibleByteOrder != nullptr); const uint8* const c = static_cast (possibleByteOrder); return c[0] == (uint8) byteOrderMarkBE1 && c[1] == (uint8) byteOrderMarkBE2; } /** Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (little endian). The pointer must not be null, and must contain at least two valid bytes. */ static bool isByteOrderMarkLittleEndian (const void* possibleByteOrder) noexcept { jassert (possibleByteOrder != nullptr); const uint8* const c = static_cast (possibleByteOrder); return c[0] == (uint8) byteOrderMarkLE1 && c[1] == (uint8) byteOrderMarkLE2; } private: CharType* data; static unsigned int findNullIndex (const CharType* const t) noexcept { unsigned int n = 0; while (t[n] != 0) ++n; return n; } }; #endif // JUCE_CHARPOINTER_UTF16_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h000066400000000000000000000350431320201440200310130ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CHARPOINTER_UTF32_H_INCLUDED #define JUCE_CHARPOINTER_UTF32_H_INCLUDED //============================================================================== /** Wraps a pointer to a null-terminated UTF-32 character string, and provides various methods to operate on the data. @see CharPointer_UTF8, CharPointer_UTF16 */ class CharPointer_UTF32 { public: typedef juce_wchar CharType; inline explicit CharPointer_UTF32 (const CharType* const rawPointer) noexcept : data (const_cast (rawPointer)) { } inline CharPointer_UTF32 (const CharPointer_UTF32& other) noexcept : data (other.data) { } inline CharPointer_UTF32 operator= (CharPointer_UTF32 other) noexcept { data = other.data; return *this; } inline CharPointer_UTF32 operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ inline bool operator== (CharPointer_UTF32 other) const noexcept { return data == other.data; } inline bool operator!= (CharPointer_UTF32 other) const noexcept { return data != other.data; } inline bool operator<= (CharPointer_UTF32 other) const noexcept { return data <= other.data; } inline bool operator< (CharPointer_UTF32 other) const noexcept { return data < other.data; } inline bool operator>= (CharPointer_UTF32 other) const noexcept { return data >= other.data; } inline bool operator> (CharPointer_UTF32 other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } /** Returns the address that this pointer is pointing to. */ inline operator const CharType*() const noexcept { return data; } /** Returns true if this pointer is pointing to a null character. */ inline bool isEmpty() const noexcept { return *data == 0; } /** Returns the unicode character that this pointer is pointing to. */ inline juce_wchar operator*() const noexcept { return *data; } /** Moves this pointer along to the next character in the string. */ inline CharPointer_UTF32 operator++() noexcept { ++data; return *this; } /** Moves this pointer to the previous character in the string. */ inline CharPointer_UTF32 operator--() noexcept { --data; return *this; } /** Returns the character that this pointer is currently pointing to, and then advances the pointer to point to the next character. */ inline juce_wchar getAndAdvance() noexcept { return *data++; } /** Moves this pointer along to the next character in the string. */ CharPointer_UTF32 operator++ (int) noexcept { CharPointer_UTF32 temp (*this); ++data; return temp; } /** Moves this pointer forwards by the specified number of characters. */ inline void operator+= (const int numToSkip) noexcept { data += numToSkip; } inline void operator-= (const int numToSkip) noexcept { data -= numToSkip; } /** Returns the character at a given character index from the start of the string. */ inline juce_wchar& operator[] (const int characterIndex) const noexcept { return data [characterIndex]; } /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ CharPointer_UTF32 operator+ (const int numToSkip) const noexcept { return CharPointer_UTF32 (data + numToSkip); } /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ CharPointer_UTF32 operator- (const int numToSkip) const noexcept { return CharPointer_UTF32 (data - numToSkip); } /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ inline void write (const juce_wchar charToWrite) noexcept { *data++ = charToWrite; } inline void replaceChar (const juce_wchar newChar) noexcept { *data = newChar; } /** Writes a null character to this string (leaving the pointer's position unchanged). */ inline void writeNull() const noexcept { *data = 0; } /** Returns the number of characters in this string. */ size_t length() const noexcept { #if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID return wcslen (data); #else size_t n = 0; while (data[n] != 0) ++n; return n; #endif } /** Returns the number of characters in this string, or the given value, whichever is lower. */ size_t lengthUpTo (const size_t maxCharsToCount) const noexcept { return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ size_t lengthUpTo (const CharPointer_UTF32 end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } /** Returns the number of bytes that are used to represent this string. This includes the terminating null character. */ size_t sizeInBytes() const noexcept { return sizeof (CharType) * (length() + 1); } /** Returns the number of bytes that would be needed to represent the given unicode character in this encoding format. */ static inline size_t getBytesRequiredFor (const juce_wchar) noexcept { return sizeof (CharType); } /** Returns the number of bytes that would be needed to represent the given string in this encoding format. The value returned does NOT include the terminating null character. */ template static size_t getBytesRequiredFor (const CharPointer text) noexcept { return sizeof (CharType) * text.length(); } /** Returns a pointer to the null character that terminates this string. */ CharPointer_UTF32 findTerminatingNull() const noexcept { return CharPointer_UTF32 (data + length()); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ template void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ void writeAll (const CharPointer_UTF32 src) noexcept { const CharType* s = src.data; while ((*data = *s) != 0) { ++data; ++s; } } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxDestBytes parameter specifies the maximum number of bytes that can be written to the destination buffer before stopping. */ template size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxChars parameter specifies the maximum number of characters that can be written to the destination buffer before stopping (including the terminating null). */ template void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } #if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID /** Compares this string with another one. */ int compare (const CharPointer_UTF32 other) const noexcept { return wcscmp (data, other.data); } #endif /** Compares this string with another one, up to a specified number of characters. */ template int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one. */ template int compareIgnoreCase (const CharPointer other) const { return CharacterFunctions::compareIgnoreCase (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } /** Returns the character index of a substring, or -1 if it isn't found. */ template int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind) const noexcept { int i = 0; while (data[i] != 0) { if (data[i] == charToFind) return i; ++i; } return -1; } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind, const bool ignoreCase) const noexcept { return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) : CharacterFunctions::indexOfChar (*this, charToFind); } /** Returns true if the first character of this string is whitespace. */ bool isWhitespace() const { return CharacterFunctions::isWhitespace (*data) != 0; } /** Returns true if the first character of this string is a digit. */ bool isDigit() const { return CharacterFunctions::isDigit (*data) != 0; } /** Returns true if the first character of this string is a letter. */ bool isLetter() const { return CharacterFunctions::isLetter (*data) != 0; } /** Returns true if the first character of this string is a letter or digit. */ bool isLetterOrDigit() const { return CharacterFunctions::isLetterOrDigit (*data) != 0; } /** Returns true if the first character of this string is upper-case. */ bool isUpperCase() const { return CharacterFunctions::isUpperCase (*data) != 0; } /** Returns true if the first character of this string is lower-case. */ bool isLowerCase() const { return CharacterFunctions::isLowerCase (*data) != 0; } /** Returns an upper-case version of the first character of this string. */ juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase (*data); } /** Returns a lower-case version of the first character of this string. */ juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase (*data); } /** Parses this string as a 32-bit integer. */ int getIntValue32() const noexcept { return CharacterFunctions::getIntValue (*this); } /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { return CharacterFunctions::getIntValue (*this); } /** Parses this string as a floating point double. */ double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); } /** Returns the first non-whitespace character in the string. */ CharPointer_UTF32 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { return ((unsigned int) character) < (unsigned int) 0x10ffff; } /** Returns true if this data contains a valid string in this encoding. */ static bool isValidString (const CharType* dataToTest, int maxBytesToRead) { maxBytesToRead /= (int) sizeof (CharType); while (--maxBytesToRead >= 0 && *dataToTest != 0) if (! canRepresent (*dataToTest++)) return false; return true; } /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF32 atomicSwap (const CharPointer_UTF32 newValue) { return CharPointer_UTF32 (reinterpret_cast &> (data).exchange (newValue.data)); } private: CharType* data; }; #endif // JUCE_CHARPOINTER_UTF32_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h000066400000000000000000000466331320201440200307450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CHARPOINTER_UTF8_H_INCLUDED #define JUCE_CHARPOINTER_UTF8_H_INCLUDED //============================================================================== /** Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate on the data. @see CharPointer_UTF16, CharPointer_UTF32 */ class CharPointer_UTF8 { public: typedef char CharType; inline explicit CharPointer_UTF8 (const CharType* const rawPointer) noexcept : data (const_cast (rawPointer)) { } inline CharPointer_UTF8 (const CharPointer_UTF8& other) noexcept : data (other.data) { } inline CharPointer_UTF8 operator= (CharPointer_UTF8 other) noexcept { data = other.data; return *this; } inline CharPointer_UTF8 operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ inline bool operator== (CharPointer_UTF8 other) const noexcept { return data == other.data; } inline bool operator!= (CharPointer_UTF8 other) const noexcept { return data != other.data; } inline bool operator<= (CharPointer_UTF8 other) const noexcept { return data <= other.data; } inline bool operator< (CharPointer_UTF8 other) const noexcept { return data < other.data; } inline bool operator>= (CharPointer_UTF8 other) const noexcept { return data >= other.data; } inline bool operator> (CharPointer_UTF8 other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } /** Returns the address that this pointer is pointing to. */ inline operator const CharType*() const noexcept { return data; } /** Returns true if this pointer is pointing to a null character. */ inline bool isEmpty() const noexcept { return *data == 0; } /** Returns the unicode character that this pointer is pointing to. */ juce_wchar operator*() const noexcept { const signed char byte = (signed char) *data; if (byte >= 0) return (juce_wchar) (uint8) byte; uint32 n = (uint32) (uint8) byte; uint32 mask = 0x7f; uint32 bit = 0x40; size_t numExtraValues = 0; while ((n & bit) != 0 && bit > 0x10) { mask >>= 1; ++numExtraValues; bit >>= 1; } n &= mask; for (size_t i = 1; i <= numExtraValues; ++i) { const uint8 nextByte = (uint8) data [i]; if ((nextByte & 0xc0) != 0x80) break; n <<= 6; n |= (nextByte & 0x3f); } return (juce_wchar) n; } /** Moves this pointer along to the next character in the string. */ CharPointer_UTF8& operator++() noexcept { jassert (*data != 0); // trying to advance past the end of the string? const signed char n = (signed char) *data++; if (n < 0) { juce_wchar bit = 0x40; while ((n & bit) != 0 && bit > 0x8) { ++data; bit >>= 1; } } return *this; } /** Moves this pointer back to the previous character in the string. */ CharPointer_UTF8 operator--() noexcept { int count = 0; while ((*--data & 0xc0) == 0x80 && ++count < 4) {} return *this; } /** Returns the character that this pointer is currently pointing to, and then advances the pointer to point to the next character. */ juce_wchar getAndAdvance() noexcept { const signed char byte = (signed char) *data++; if (byte >= 0) return (juce_wchar) (uint8) byte; uint32 n = (uint32) (uint8) byte; uint32 mask = 0x7f; uint32 bit = 0x40; int numExtraValues = 0; while ((n & bit) != 0 && bit > 0x8) { mask >>= 1; ++numExtraValues; bit >>= 1; } n &= mask; while (--numExtraValues >= 0) { const uint32 nextByte = (uint32) (uint8) *data; if ((nextByte & 0xc0) != 0x80) break; ++data; n <<= 6; n |= (nextByte & 0x3f); } return (juce_wchar) n; } /** Moves this pointer along to the next character in the string. */ CharPointer_UTF8 operator++ (int) noexcept { CharPointer_UTF8 temp (*this); ++*this; return temp; } /** Moves this pointer forwards by the specified number of characters. */ void operator+= (int numToSkip) noexcept { if (numToSkip < 0) { while (++numToSkip <= 0) --*this; } else { while (--numToSkip >= 0) ++*this; } } /** Moves this pointer backwards by the specified number of characters. */ void operator-= (int numToSkip) noexcept { operator+= (-numToSkip); } /** Returns the character at a given character index from the start of the string. */ juce_wchar operator[] (int characterIndex) const noexcept { CharPointer_UTF8 p (*this); p += characterIndex; return *p; } /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ CharPointer_UTF8 operator+ (int numToSkip) const noexcept { CharPointer_UTF8 p (*this); p += numToSkip; return p; } /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ CharPointer_UTF8 operator- (int numToSkip) const noexcept { CharPointer_UTF8 p (*this); p += -numToSkip; return p; } /** Returns the number of characters in this string. */ size_t length() const noexcept { const CharType* d = data; size_t count = 0; for (;;) { const uint32 n = (uint32) (uint8) *d++; if ((n & 0x80) != 0) { while ((*d & 0xc0) == 0x80) ++d; } else if (n == 0) break; ++count; } return count; } /** Returns the number of characters in this string, or the given value, whichever is lower. */ size_t lengthUpTo (const size_t maxCharsToCount) const noexcept { return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ size_t lengthUpTo (const CharPointer_UTF8 end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } /** Returns the number of bytes that are used to represent this string. This includes the terminating null character. */ size_t sizeInBytes() const noexcept { jassert (data != nullptr); return strlen (data) + 1; } /** Returns the number of bytes that would be needed to represent the given unicode character in this encoding format. */ static size_t getBytesRequiredFor (const juce_wchar charToWrite) noexcept { size_t num = 1; const uint32 c = (uint32) charToWrite; if (c >= 0x80) { ++num; if (c >= 0x800) { ++num; if (c >= 0x10000) ++num; } } return num; } /** Returns the number of bytes that would be needed to represent the given string in this encoding format. The value returned does NOT include the terminating null character. */ template static size_t getBytesRequiredFor (CharPointer text) noexcept { size_t count = 0; juce_wchar n; while ((n = text.getAndAdvance()) != 0) count += getBytesRequiredFor (n); return count; } /** Returns a pointer to the null character that terminates this string. */ CharPointer_UTF8 findTerminatingNull() const noexcept { return CharPointer_UTF8 (data + strlen (data)); } /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ void write (const juce_wchar charToWrite) noexcept { const uint32 c = (uint32) charToWrite; if (c >= 0x80) { int numExtraBytes = 1; if (c >= 0x800) { ++numExtraBytes; if (c >= 0x10000) ++numExtraBytes; } *data++ = (CharType) ((uint32) (0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6))); while (--numExtraBytes >= 0) *data++ = (CharType) (0x80 | (0x3f & (c >> (numExtraBytes * 6)))); } else { *data++ = (CharType) c; } } /** Writes a null character to this string (leaving the pointer's position unchanged). */ inline void writeNull() const noexcept { *data = 0; } /** Copies a source string to this pointer, advancing this pointer as it goes. */ template void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ void writeAll (const CharPointer_UTF8 src) noexcept { const CharType* s = src.data; while ((*data = *s) != 0) { ++data; ++s; } } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxDestBytes parameter specifies the maximum number of bytes that can be written to the destination buffer before stopping. */ template size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } /** Copies a source string to this pointer, advancing this pointer as it goes. The maxChars parameter specifies the maximum number of characters that can be written to the destination buffer before stopping (including the terminating null). */ template void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one. */ template int compareIgnoreCase (const CharPointer other) const noexcept { return CharacterFunctions::compareIgnoreCase (*this, other); } /** Compares this string with another one. */ int compareIgnoreCase (const CharPointer_UTF8 other) const noexcept { #if JUCE_MSVC return stricmp (data, other.data); #elif JUCE_MINGW return CharacterFunctions::compareIgnoreCase (*this, other); #else return strcasecmp (data, other.data); #endif } /** Compares this string with another one, up to a specified number of characters. */ template int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } /** Returns the character index of a substring, or -1 if it isn't found. */ template int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind) const noexcept { return CharacterFunctions::indexOfChar (*this, charToFind); } /** Returns the character index of a unicode character, or -1 if it isn't found. */ int indexOf (const juce_wchar charToFind, const bool ignoreCase) const noexcept { return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) : CharacterFunctions::indexOfChar (*this, charToFind); } /** Returns true if the first character of this string is whitespace. */ bool isWhitespace() const noexcept { const CharType c = *data; return c == ' ' || (c <= 13 && c >= 9); } /** Returns true if the first character of this string is a digit. */ bool isDigit() const noexcept { const CharType c = *data; return c >= '0' && c <= '9'; } /** Returns true if the first character of this string is a letter. */ bool isLetter() const noexcept { return CharacterFunctions::isLetter (operator*()) != 0; } /** Returns true if the first character of this string is a letter or digit. */ bool isLetterOrDigit() const noexcept { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; } /** Returns true if the first character of this string is upper-case. */ bool isUpperCase() const noexcept { return CharacterFunctions::isUpperCase (operator*()) != 0; } /** Returns true if the first character of this string is lower-case. */ bool isLowerCase() const noexcept { return CharacterFunctions::isLowerCase (operator*()) != 0; } /** Returns an upper-case version of the first character of this string. */ juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase (operator*()); } /** Returns a lower-case version of the first character of this string. */ juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase (operator*()); } /** Parses this string as a 32-bit integer. */ int getIntValue32() const noexcept { return atoi (data); } /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { #if JUCE_LINUX || JUCE_ANDROID || JUCE_MINGW return atoll (data); #elif JUCE_WINDOWS return _atoi64 (data); #else return CharacterFunctions::getIntValue (*this); #endif } /** Parses this string as a floating point double. */ double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); } /** Returns the first non-whitespace character in the string. */ CharPointer_UTF8 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { return ((unsigned int) character) < (unsigned int) 0x10ffff; } /** Returns true if this data contains a valid string in this encoding. */ static bool isValidString (const CharType* dataToTest, int maxBytesToRead) { while (--maxBytesToRead >= 0 && *dataToTest != 0) { const signed char byte = (signed char) *dataToTest++; if (byte < 0) { int bit = 0x40; int numExtraValues = 0; while ((byte & bit) != 0) { if (bit < 8) return false; ++numExtraValues; bit >>= 1; if (bit == 8 && (numExtraValues > maxBytesToRead || *CharPointer_UTF8 (dataToTest - 1) > 0x10ffff)) return false; } maxBytesToRead -= numExtraValues; if (maxBytesToRead < 0) return false; while (--numExtraValues >= 0) if ((*dataToTest++ & 0xc0) != 0x80) return false; } } return true; } /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF8 atomicSwap (const CharPointer_UTF8 newValue) { return CharPointer_UTF8 (reinterpret_cast &> (data).exchange (newValue.data)); } /** These values are the byte-order mark (BOM) values for a UTF-8 stream. */ enum { byteOrderMark1 = 0xef, byteOrderMark2 = 0xbb, byteOrderMark3 = 0xbf }; /** Returns true if the first three bytes in this pointer are the UTF8 byte-order mark (BOM). The pointer must not be null, and must point to at least 3 valid bytes. */ static bool isByteOrderMark (const void* possibleByteOrder) noexcept { jassert (possibleByteOrder != nullptr); const uint8* const c = static_cast (possibleByteOrder); return c[0] == (uint8) byteOrderMark1 && c[1] == (uint8) byteOrderMark2 && c[2] == (uint8) byteOrderMark3; } private: CharType* data; }; #endif // JUCE_CHARPOINTER_UTF8_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp000066400000000000000000000111511320201440200320240ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ //============================================================================== #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4996) #endif juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) noexcept { return (juce_wchar) towupper ((wint_t) character); } juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) noexcept { return (juce_wchar) towlower ((wint_t) character); } bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept { #if JUCE_WINDOWS return iswupper ((wint_t) character) != 0; #else return toLowerCase (character) != character; #endif } bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept { #if JUCE_WINDOWS return iswlower ((wint_t) character) != 0; #else return toUpperCase (character) != character; #endif } #if JUCE_MSVC #pragma warning (pop) #endif //============================================================================== bool CharacterFunctions::isWhitespace (const char character) noexcept { return character == ' ' || (character <= 13 && character >= 9); } bool CharacterFunctions::isWhitespace (const juce_wchar character) noexcept { return iswspace ((wint_t) character) != 0; } bool CharacterFunctions::isDigit (const char character) noexcept { return (character >= '0' && character <= '9'); } bool CharacterFunctions::isDigit (const juce_wchar character) noexcept { return iswdigit ((wint_t) character) != 0; } bool CharacterFunctions::isLetter (const char character) noexcept { return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z'); } bool CharacterFunctions::isLetter (const juce_wchar character) noexcept { return iswalpha ((wint_t) character) != 0; } bool CharacterFunctions::isLetterOrDigit (const char character) noexcept { return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9'); } bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) noexcept { return iswalnum ((wint_t) character) != 0; } int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept { unsigned int d = (unsigned int) digit - '0'; if (d < (unsigned int) 10) return (int) d; d += (unsigned int) ('0' - 'a'); if (d < (unsigned int) 6) return (int) d + 10; d += (unsigned int) ('a' - 'A'); if (d < (unsigned int) 6) return (int) d + 10; return -1; } double CharacterFunctions::mulexp10 (const double value, int exponent) noexcept { if (exponent == 0) return value; if (value == 0) return 0; const bool negative = (exponent < 0); if (negative) exponent = -exponent; double result = 1.0, power = 10.0; for (int bit = 1; exponent != 0; bit <<= 1) { if ((exponent & bit) != 0) { exponent ^= bit; result *= power; if (exponent == 0) break; } power *= power; } return negative ? (value / result) : (value * result); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h000066400000000000000000000524711320201440200315030ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CHARACTERFUNCTIONS_H_INCLUDED #define JUCE_CHARACTERFUNCTIONS_H_INCLUDED //============================================================================== #if JUCE_WINDOWS && ! DOXYGEN #define JUCE_NATIVE_WCHAR_IS_UTF8 0 #define JUCE_NATIVE_WCHAR_IS_UTF16 1 #define JUCE_NATIVE_WCHAR_IS_UTF32 0 #else /** This macro will be set to 1 if the compiler's native wchar_t is an 8-bit type. */ #define JUCE_NATIVE_WCHAR_IS_UTF8 0 /** This macro will be set to 1 if the compiler's native wchar_t is a 16-bit type. */ #define JUCE_NATIVE_WCHAR_IS_UTF16 0 /** This macro will be set to 1 if the compiler's native wchar_t is a 32-bit type. */ #define JUCE_NATIVE_WCHAR_IS_UTF32 1 #endif #if JUCE_NATIVE_WCHAR_IS_UTF32 || DOXYGEN /** A platform-independent 32-bit unicode character type. */ typedef wchar_t juce_wchar; #else typedef uint32 juce_wchar; #endif #ifndef DOXYGEN /** This macro is deprecated, but preserved for compatibility with old code. */ #define JUCE_T(stringLiteral) (L##stringLiteral) #endif #if JUCE_DEFINE_T_MACRO /** The 'T' macro is an alternative for using the "L" prefix in front of a string literal. This macro is deprecated, but available for compatibility with old code if you set JUCE_DEFINE_T_MACRO = 1. The fastest, most portable and best way to write your string literals is as standard char strings, using escaped utf-8 character sequences for extended characters, rather than trying to store them as wide-char strings. */ #define T(stringLiteral) JUCE_T(stringLiteral) #endif //============================================================================== /** A collection of functions for manipulating characters and character strings. Most of these methods are designed for internal use by the String and CharPointer classes, but some of them may be useful to call directly. @see String, CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32 */ class JUCE_API CharacterFunctions { public: //============================================================================== /** Converts a character to upper-case. */ static juce_wchar toUpperCase (juce_wchar character) noexcept; /** Converts a character to lower-case. */ static juce_wchar toLowerCase (juce_wchar character) noexcept; /** Checks whether a unicode character is upper-case. */ static bool isUpperCase (juce_wchar character) noexcept; /** Checks whether a unicode character is lower-case. */ static bool isLowerCase (juce_wchar character) noexcept; /** Checks whether a character is whitespace. */ static bool isWhitespace (char character) noexcept; /** Checks whether a character is whitespace. */ static bool isWhitespace (juce_wchar character) noexcept; /** Checks whether a character is a digit. */ static bool isDigit (char character) noexcept; /** Checks whether a character is a digit. */ static bool isDigit (juce_wchar character) noexcept; /** Checks whether a character is alphabetic. */ static bool isLetter (char character) noexcept; /** Checks whether a character is alphabetic. */ static bool isLetter (juce_wchar character) noexcept; /** Checks whether a character is alphabetic or numeric. */ static bool isLetterOrDigit (char character) noexcept; /** Checks whether a character is alphabetic or numeric. */ static bool isLetterOrDigit (juce_wchar character) noexcept; /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */ static int getHexDigitValue (juce_wchar digit) noexcept; //============================================================================== /** Parses a character string to read a floating-point number. Note that this will advance the pointer that is passed in, leaving it at the end of the number. */ template static double readDoubleValue (CharPointerType& text) noexcept { double result[3] = { 0 }, accumulator[2] = { 0 }; int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 }; int exponent = 0, decPointIndex = 0, digit = 0; int lastDigit = 0, numSignificantDigits = 0; bool isNegative = false, digitsFound = false; const int maxSignificantDigits = 15 + 2; text = text.findEndOfWhitespace(); juce_wchar c = *text; switch (c) { case '-': isNegative = true; // fall-through.. case '+': c = *++text; } switch (c) { case 'n': case 'N': if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N')) return std::numeric_limits::quiet_NaN(); break; case 'i': case 'I': if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F')) return std::numeric_limits::infinity(); break; } for (;;) { if (text.isDigit()) { lastDigit = digit; digit = (int) text.getAndAdvance() - '0'; digitsFound = true; if (decPointIndex != 0) exponentAdjustment[1]++; if (numSignificantDigits == 0 && digit == 0) continue; if (++numSignificantDigits > maxSignificantDigits) { if (digit > 5) ++accumulator [decPointIndex]; else if (digit == 5 && (lastDigit & 1) != 0) ++accumulator [decPointIndex]; if (decPointIndex > 0) exponentAdjustment[1]--; else exponentAdjustment[0]++; while (text.isDigit()) { ++text; if (decPointIndex == 0) exponentAdjustment[0]++; } } else { const double maxAccumulatorValue = (double) ((std::numeric_limits::max() - 9) / 10); if (accumulator [decPointIndex] > maxAccumulatorValue) { result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex]) + accumulator [decPointIndex]; accumulator [decPointIndex] = 0; exponentAccumulator [decPointIndex] = 0; } accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit; exponentAccumulator [decPointIndex]++; } } else if (decPointIndex == 0 && *text == '.') { ++text; decPointIndex = 1; if (numSignificantDigits > maxSignificantDigits) { while (text.isDigit()) ++text; break; } } else { break; } } result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0]; if (decPointIndex != 0) result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1]; c = *text; if ((c == 'e' || c == 'E') && digitsFound) { bool negativeExponent = false; switch (*++text) { case '-': negativeExponent = true; // fall-through.. case '+': ++text; } while (text.isDigit()) exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0'); if (negativeExponent) exponent = -exponent; } double r = mulexp10 (result[0], exponent + exponentAdjustment[0]); if (decPointIndex != 0) r += mulexp10 (result[1], exponent - exponentAdjustment[1]); return isNegative ? -r : r; } /** Parses a character string, to read a floating-point value. */ template static double getDoubleValue (CharPointerType text) noexcept { return readDoubleValue (text); } //============================================================================== /** Parses a character string, to read an integer value. */ template static IntType getIntValue (const CharPointerType text) noexcept { IntType v = 0; CharPointerType s (text.findEndOfWhitespace()); const bool isNeg = *s == '-'; if (isNeg) ++s; for (;;) { const juce_wchar c = s.getAndAdvance(); if (c >= '0' && c <= '9') v = v * 10 + (IntType) (c - '0'); else break; } return isNeg ? -v : v; } template struct HexParser { template static ResultType parse (CharPointerType t) noexcept { ResultType result = 0; while (! t.isEmpty()) { const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (hexValue >= 0) result = (result << 4) | hexValue; } return result; } }; //============================================================================== /** Counts the number of characters in a given string, stopping if the count exceeds a specified limit. */ template static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) noexcept { size_t len = 0; while (len < maxCharsToCount && text.getAndAdvance() != 0) ++len; return len; } /** Counts the number of characters in a given string, stopping if the count exceeds a specified end-pointer. */ template static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept { size_t len = 0; while (start < end && start.getAndAdvance() != 0) ++len; return len; } /** Copies null-terminated characters from one string to another. */ template static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept { for (;;) { const juce_wchar c = src.getAndAdvance(); if (c == 0) break; dest.write (c); } dest.writeNull(); } /** Copies characters from one string to another, up to a null terminator or a given byte size limit. */ template static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept { typename DestCharPointerType::CharType const* const startAddress = dest.getAddress(); ssize_t maxBytes = (ssize_t) maxBytesToWrite; maxBytes -= sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) for (;;) { const juce_wchar c = src.getAndAdvance(); const size_t bytesNeeded = DestCharPointerType::getBytesRequiredFor (c); maxBytes -= bytesNeeded; if (c == 0 || maxBytes < 0) break; dest.write (c); } dest.writeNull(); return (size_t) getAddressDifference (dest.getAddress(), startAddress) + sizeof (typename DestCharPointerType::CharType); } /** Copies characters from one string to another, up to a null terminator or a given maximum number of characters. */ template static void copyWithCharLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) noexcept { while (--maxChars > 0) { const juce_wchar c = src.getAndAdvance(); if (c == 0) break; dest.write (c); } dest.writeNull(); } /** Compares two null-terminated character strings. */ template static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept { for (;;) { const int c1 = (int) s1.getAndAdvance(); const int c2 = (int) s2.getAndAdvance(); const int diff = c1 - c2; if (diff != 0) return diff < 0 ? -1 : 1; if (c1 == 0) break; } return 0; } /** Compares two null-terminated character strings, up to a given number of characters. */ template static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept { while (--maxChars >= 0) { const int c1 = (int) s1.getAndAdvance(); const int c2 = (int) s2.getAndAdvance(); const int diff = c1 - c2; if (diff != 0) return diff < 0 ? -1 : 1; if (c1 == 0) break; } return 0; } /** Compares two null-terminated character strings, using a case-independant match. */ template static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept { for (;;) { const int c1 = (int) s1.toUpperCase(); const int c2 = (int) s2.toUpperCase(); const int diff = c1 - c2; if (diff != 0) return diff < 0 ? -1 : 1; if (c1 == 0) break; ++s1; ++s2; } return 0; } /** Compares two null-terminated character strings, using a case-independent match. */ template static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept { while (--maxChars >= 0) { const int c1 = (int) s1.toUpperCase(); const int c2 = (int) s2.toUpperCase(); const int diff = c1 - c2; if (diff != 0) return diff < 0 ? -1 : 1; if (c1 == 0) break; ++s1; ++s2; } return 0; } /** Finds the character index of a given substring in another string. Returns -1 if the substring is not found. */ template static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept { int index = 0; const int substringLength = (int) substringToLookFor.length(); for (;;) { if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0) return index; if (textToSearch.getAndAdvance() == 0) return -1; ++index; } } /** Returns a pointer to the first occurrence of a substring in a string. If the substring is not found, this will return a pointer to the string's null terminator. */ template static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept { const int substringLength = (int) substringToLookFor.length(); while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0 && ! textToSearch.isEmpty()) ++textToSearch; return textToSearch; } /** Returns a pointer to the first occurrence of a substring in a string. If the substring is not found, this will return a pointer to the string's null terminator. */ template static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept { for (;; ++textToSearch) { const juce_wchar c = *textToSearch; if (c == charToLookFor || c == 0) break; } return textToSearch; } /** Finds the character index of a given substring in another string, using a case-independent match. Returns -1 if the substring is not found. */ template static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept { int index = 0; const int needleLength = (int) needle.length(); for (;;) { if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0) return index; if (haystack.getAndAdvance() == 0) return -1; ++index; } } /** Finds the character index of a given character in another string. Returns -1 if the character is not found. */ template static int indexOfChar (Type text, const juce_wchar charToFind) noexcept { int i = 0; while (! text.isEmpty()) { if (text.getAndAdvance() == charToFind) return i; ++i; } return -1; } /** Finds the character index of a given character in another string, using a case-independent match. Returns -1 if the character is not found. */ template static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) noexcept { charToFind = CharacterFunctions::toLowerCase (charToFind); int i = 0; while (! text.isEmpty()) { if (text.toLowerCase() == charToFind) return i; ++text; ++i; } return -1; } /** Returns a pointer to the first non-whitespace character in a string. If the string contains only whitespace, this will return a pointer to its null terminator. */ template static Type findEndOfWhitespace (Type text) noexcept { while (text.isWhitespace()) ++text; return text; } /** Returns a pointer to the first character in the string which is found in the breakCharacters string. */ template static Type findEndOfToken (Type text, const BreakType breakCharacters, const Type quoteCharacters) { juce_wchar currentQuoteChar = 0; while (! text.isEmpty()) { const juce_wchar c = text.getAndAdvance(); if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0) { --text; break; } if (quoteCharacters.indexOf (c) >= 0) { if (currentQuoteChar == 0) currentQuoteChar = c; else if (currentQuoteChar == c) currentQuoteChar = 0; } } return text; } private: static double mulexp10 (const double value, int exponent) noexcept; }; #endif // JUCE_CHARACTERFUNCTIONS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp000066400000000000000000000056111320201440200303250ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ Identifier::Identifier() noexcept {} Identifier::~Identifier() noexcept {} Identifier::Identifier (const Identifier& other) noexcept : name (other.name) {} #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Identifier::Identifier (Identifier&& other) noexcept : name (static_cast (other.name)) {} Identifier& Identifier::operator= (Identifier&& other) noexcept { name = static_cast (other.name); return *this; } #endif Identifier& Identifier::operator= (const Identifier& other) noexcept { name = other.name; return *this; } Identifier::Identifier (const String& nm) : name (StringPool::getGlobalPool().getPooledString (nm)) { // An Identifier cannot be created from an empty string! jassert (nm.isNotEmpty()); } Identifier::Identifier (const char* nm) : name (StringPool::getGlobalPool().getPooledString (nm)) { // An Identifier cannot be created from an empty string! jassert (nm != nullptr && nm[0] != 0); } Identifier::Identifier (String::CharPointerType start, String::CharPointerType end) : name (StringPool::getGlobalPool().getPooledString (start, end)) { // An Identifier cannot be created from an empty string! jassert (start < end); } Identifier Identifier::null; bool Identifier::isValidIdentifier (const String& possibleIdentifier) noexcept { return possibleIdentifier.isNotEmpty() && possibleIdentifier.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:#@$%"); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h000066400000000000000000000130161320201440200277700ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_IDENTIFIER_H_INCLUDED #define JUCE_IDENTIFIER_H_INCLUDED //============================================================================== /** Represents a string identifier, designed for accessing properties by name. Comparing two Identifier objects is very fast (an O(1) operation), but creating them can be slower than just using a String directly, so the optimal way to use them is to keep some static Identifier objects for the things you use often. @see NamedValueSet, ValueTree */ class JUCE_API Identifier { public: /** Creates a null identifier. */ Identifier() noexcept; /** Creates an identifier with a specified name. Because this name may need to be used in contexts such as script variables or XML tags, it must only contain ascii letters and digits, or the underscore character. */ Identifier (const char* name); /** Creates an identifier with a specified name. Because this name may need to be used in contexts such as script variables or XML tags, it must only contain ascii letters and digits, or the underscore character. */ Identifier (const String& name); /** Creates an identifier with a specified name. Because this name may need to be used in contexts such as script variables or XML tags, it must only contain ascii letters and digits, or the underscore character. */ Identifier (String::CharPointerType nameStart, String::CharPointerType nameEnd); /** Creates a copy of another identifier. */ Identifier (const Identifier& other) noexcept; /** Creates a copy of another identifier. */ Identifier& operator= (const Identifier& other) noexcept; #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS /** Creates a copy of another identifier. */ Identifier (Identifier&& other) noexcept; /** Creates a copy of another identifier. */ Identifier& operator= (Identifier&& other) noexcept; #endif /** Destructor */ ~Identifier() noexcept; /** Compares two identifiers. This is a very fast operation. */ inline bool operator== (const Identifier& other) const noexcept { return name.getCharPointer() == other.name.getCharPointer(); } /** Compares two identifiers. This is a very fast operation. */ inline bool operator!= (const Identifier& other) const noexcept { return name.getCharPointer() != other.name.getCharPointer(); } /** Compares the identifier with a string. */ inline bool operator== (StringRef other) const noexcept { return name == other; } /** Compares the identifier with a string. */ inline bool operator!= (StringRef other) const noexcept { return name != other; } /** Returns this identifier as a string. */ const String& toString() const noexcept { return name; } /** Returns this identifier's raw string pointer. */ operator String::CharPointerType() const noexcept { return name.getCharPointer(); } /** Returns this identifier's raw string pointer. */ String::CharPointerType getCharPointer() const noexcept { return name.getCharPointer(); } /** Returns this identifier as a StringRef. */ operator StringRef() const noexcept { return name; } /** Returns true if this Identifier is not null */ bool isValid() const noexcept { return name.isNotEmpty(); } /** Returns true if this Identifier is null */ bool isNull() const noexcept { return name.isEmpty(); } /** A null identifier. */ static Identifier null; /** Checks a given string for characters that might not be valid in an Identifier. Since Identifiers are used as a script variables and XML attributes, they should only contain alphanumeric characters, underscores, or the '-' and ':' characters. */ static bool isValidIdentifier (const String& possibleIdentifier) noexcept; private: String name; }; #endif // JUCE_IDENTIFIER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp000066400000000000000000000162061320201440200315160ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ LocalisedStrings::LocalisedStrings (const String& fileContents, bool ignoreCase) { loadFromText (fileContents, ignoreCase); } LocalisedStrings::LocalisedStrings (const File& fileToLoad, bool ignoreCase) { loadFromText (fileToLoad.loadFileAsString(), ignoreCase); } LocalisedStrings::LocalisedStrings (const LocalisedStrings& other) : languageName (other.languageName), countryCodes (other.countryCodes), translations (other.translations), fallback (createCopyIfNotNull (other.fallback.get())) { } LocalisedStrings& LocalisedStrings::operator= (const LocalisedStrings& other) { languageName = other.languageName; countryCodes = other.countryCodes; translations = other.translations; fallback = createCopyIfNotNull (other.fallback.get()); return *this; } LocalisedStrings::~LocalisedStrings() { } //============================================================================== String LocalisedStrings::translate (const String& text) const { if (fallback != nullptr && ! translations.containsKey (text)) return fallback->translate (text); return translations.getValue (text, text); } String LocalisedStrings::translate (const String& text, const String& resultIfNotFound) const { if (fallback != nullptr && ! translations.containsKey (text)) return fallback->translate (text, resultIfNotFound); return translations.getValue (text, resultIfNotFound); } namespace { #if JUCE_CHECK_MEMORY_LEAKS // By using this object to force a LocalisedStrings object to be created // before the currentMappings object, we can force the static order-of-destruction to // delete the currentMappings object first, which avoids a bogus leak warning. // (Oddly, just creating a LocalisedStrings on the stack doesn't work in gcc, it // has to be created with 'new' for this to work..) struct LeakAvoidanceTrick { LeakAvoidanceTrick() { const ScopedPointer dummy (new LocalisedStrings (String(), false)); } }; LeakAvoidanceTrick leakAvoidanceTrick; #endif SpinLock currentMappingsLock; ScopedPointer currentMappings; static int findCloseQuote (const String& text, int startPos) { juce_wchar lastChar = 0; String::CharPointerType t (text.getCharPointer() + startPos); for (;;) { const juce_wchar c = t.getAndAdvance(); if (c == 0 || (c == '"' && lastChar != '\\')) break; lastChar = c; ++startPos; } return startPos; } static String unescapeString (const String& s) { return s.replace ("\\\"", "\"") .replace ("\\\'", "\'") .replace ("\\t", "\t") .replace ("\\r", "\r") .replace ("\\n", "\n"); } } void LocalisedStrings::loadFromText (const String& fileContents, bool ignoreCase) { translations.setIgnoresCase (ignoreCase); StringArray lines; lines.addLines (fileContents); for (int i = 0; i < lines.size(); ++i) { String line (lines[i].trim()); if (line.startsWithChar ('"')) { int closeQuote = findCloseQuote (line, 1); const String originalText (unescapeString (line.substring (1, closeQuote))); if (originalText.isNotEmpty()) { const int openingQuote = findCloseQuote (line, closeQuote + 1); closeQuote = findCloseQuote (line, openingQuote + 1); const String newText (unescapeString (line.substring (openingQuote + 1, closeQuote))); if (newText.isNotEmpty()) translations.set (originalText, newText); } } else if (line.startsWithIgnoreCase ("language:")) { languageName = line.substring (9).trim(); } else if (line.startsWithIgnoreCase ("countries:")) { countryCodes.addTokens (line.substring (10).trim(), true); countryCodes.trim(); countryCodes.removeEmptyStrings(); } } translations.minimiseStorageOverheads(); } void LocalisedStrings::addStrings (const LocalisedStrings& other) { jassert (languageName == other.languageName); jassert (countryCodes == other.countryCodes); translations.addArray (other.translations); } void LocalisedStrings::setFallback (LocalisedStrings* f) { fallback = f; } //============================================================================== void LocalisedStrings::setCurrentMappings (LocalisedStrings* newTranslations) { const SpinLock::ScopedLockType sl (currentMappingsLock); currentMappings = newTranslations; } LocalisedStrings* LocalisedStrings::getCurrentMappings() { return currentMappings; } String LocalisedStrings::translateWithCurrentMappings (const String& text) { return juce::translate (text); } String LocalisedStrings::translateWithCurrentMappings (const char* text) { return juce::translate (text); } JUCE_API String translate (const String& text) { return juce::translate (text, text); } JUCE_API String translate (const char* text) { return juce::translate (String (text)); } JUCE_API String translate (CharPointer_UTF8 text) { return juce::translate (String (text)); } JUCE_API String translate (const String& text, const String& resultIfNotFound) { const SpinLock::ScopedLockType sl (currentMappingsLock); if (const LocalisedStrings* const mappings = LocalisedStrings::getCurrentMappings()) return mappings->translate (text, resultIfNotFound); return resultIfNotFound; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h000066400000000000000000000241121320201440200311560ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_LOCALISEDSTRINGS_H_INCLUDED #define JUCE_LOCALISEDSTRINGS_H_INCLUDED //============================================================================== /** Used to convert strings to localised foreign-language versions. This is basically a look-up table of strings and their translated equivalents. It can be loaded from a text file, so that you can supply a set of localised versions of strings that you use in your app. To use it in your code, simply call the translate() method on each string that might have foreign versions, and if none is found, the method will just return the original string. The translation file should start with some lines specifying a description of the language it contains, and also a list of ISO country codes where it might be appropriate to use the file. After that, each line of the file should contain a pair of quoted strings with an '=' sign. E.g. for a french translation, the file might be: @code language: French countries: fr be mc ch lu "hello" = "bonjour" "goodbye" = "au revoir" @endcode If the strings need to contain a quote character, they can use '\"' instead, and if the first non-whitespace character on a line isn't a quote, then it's ignored, (you can use this to add comments). Note that this is a singleton class, so don't create or destroy the object directly. There's also a TRANS(text) macro defined to make it easy to use the this. E.g. @code printSomething (TRANS("hello")); @endcode This macro is used in the Juce classes themselves, so your application has a chance to intercept and translate any internal Juce text strings that might be shown. (You can easily get a list of all the messages by searching for the TRANS() macro in the Juce source code). */ class JUCE_API LocalisedStrings { public: //============================================================================== /** Creates a set of translations from the text of a translation file. When you create one of these, you can call setCurrentMappings() to make it the set of mappings that the system's using. */ LocalisedStrings (const String& fileContents, bool ignoreCaseOfKeys); /** Creates a set of translations from a file. When you create one of these, you can call setCurrentMappings() to make it the set of mappings that the system's using. */ LocalisedStrings (const File& fileToLoad, bool ignoreCaseOfKeys); LocalisedStrings (const LocalisedStrings&); LocalisedStrings& operator= (const LocalisedStrings&); /** Destructor. */ ~LocalisedStrings(); //============================================================================== /** Selects the current set of mappings to be used by the system. The object you pass in will be automatically deleted when no longer needed, so don't keep a pointer to it. You can also pass in nullptr to remove the current mappings. See also the TRANS() macro, which uses the current set to do its translation. @see translateWithCurrentMappings */ static void setCurrentMappings (LocalisedStrings* newTranslations); /** Returns the currently selected set of mappings. This is the object that was last passed to setCurrentMappings(). It may be nullptr if none has been created. */ static LocalisedStrings* getCurrentMappings(); /** Tries to translate a string using the currently selected set of mappings. If no mapping has been set, or if the mapping doesn't contain a translation for the string, this will just return the original string. See also the TRANS() macro, which uses this method to do its translation. @see setCurrentMappings, getCurrentMappings */ static String translateWithCurrentMappings (const String& text); /** Tries to translate a string using the currently selected set of mappings. If no mapping has been set, or if the mapping doesn't contain a translation for the string, this will just return the original string. See also the TRANS() macro, which uses this method to do its translation. @see setCurrentMappings, getCurrentMappings */ static String translateWithCurrentMappings (const char* text); //============================================================================== /** Attempts to look up a string and return its localised version. If the string isn't found in the list, the original string will be returned. */ String translate (const String& text) const; /** Attempts to look up a string and return its localised version. If the string isn't found in the list, the resultIfNotFound string will be returned. */ String translate (const String& text, const String& resultIfNotFound) const; /** Returns the name of the language specified in the translation file. This is specified in the file using a line starting with "language:", e.g. @code language: german @endcode */ String getLanguageName() const { return languageName; } /** Returns the list of suitable country codes listed in the translation file. These is specified in the file using a line starting with "countries:", e.g. @code countries: fr be mc ch lu @endcode The country codes are supposed to be 2-character ISO complient codes. */ const StringArray& getCountryCodes() const { return countryCodes; } /** Provides access to the actual list of mappings. */ const StringPairArray& getMappings() const { return translations; } //============================================================================== /** Adds and merges another set of translations into this set. Note that the language name and country codes of the new LocalisedStrings object must match that of this object - an assertion will be thrown if they don't match. Any existing values will have their mappings overwritten by the new ones. */ void addStrings (const LocalisedStrings&); /** Gives this object a set of strings to use as a fallback if a string isn't found. The object that is passed-in will be owned and deleted by this object when no longer needed. It can be nullptr to clear the existing fallback object. */ void setFallback (LocalisedStrings* fallbackStrings); private: //============================================================================== String languageName; StringArray countryCodes; StringPairArray translations; ScopedPointer fallback; friend struct ContainerDeletePolicy; void loadFromText (const String&, bool ignoreCase); JUCE_LEAK_DETECTOR (LocalisedStrings) }; //============================================================================== #ifndef TRANS /** Uses the LocalisedStrings class to translate the given string literal. This macro is provided for backwards-compatibility, and just calls the translate() function. In new code, it's recommended that you just call translate() directly instead, and avoid using macros. @see translate(), LocalisedStrings */ #define TRANS(stringLiteral) juce::translate (stringLiteral) #endif /** A dummy version of the TRANS macro, used to indicate a string literal that should be added to the translation file by source-code scanner tools. Wrapping a string literal in this macro has no effect, but by using it around strings that your app needs to translate at a later stage, it lets automatic code-scanning tools find this string and add it to the list of strings that need translation. */ #define NEEDS_TRANS(stringLiteral) (stringLiteral) /** Uses the LocalisedStrings class to translate the given string literal. @see LocalisedStrings */ JUCE_API String translate (const String& stringLiteral); /** Uses the LocalisedStrings class to translate the given string literal. @see LocalisedStrings */ JUCE_API String translate (const char* stringLiteral); /** Uses the LocalisedStrings class to translate the given string literal. @see LocalisedStrings */ JUCE_API String translate (CharPointer_UTF8 stringLiteral); /** Uses the LocalisedStrings class to translate the given string literal. @see LocalisedStrings */ JUCE_API String translate (const String& stringLiteral, const String& resultIfNotFound); #endif // JUCE_LOCALISEDSTRINGS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h000066400000000000000000000067571320201440200272650ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_NEWLINE_H_INCLUDED #define JUCE_NEWLINE_H_INCLUDED //============================================================================== /** This class is used for represent a new-line character sequence. To write a new-line to a stream, you can use the predefined 'newLine' variable, e.g. @code myOutputStream << "Hello World" << newLine << newLine; @endcode The exact character sequence that will be used for the new-line can be set and retrieved with OutputStream::setNewLineString() and OutputStream::getNewLineString(). */ class JUCE_API NewLine { public: /** Returns the default new-line sequence that the library uses. @see OutputStream::setNewLineString() */ static const char* getDefault() noexcept { return "\r\n"; } /** Returns the default new-line sequence that the library uses. @see getDefault() */ operator String() const { return getDefault(); } /** Returns the default new-line sequence that the library uses. @see OutputStream::setNewLineString() */ operator StringRef() const noexcept { return getDefault(); } }; //============================================================================== /** A predefined object representing a new-line, which can be written to a string or stream. To write a new-line to a stream, you can use the predefined 'newLine' variable like this: @code myOutputStream << "Hello World" << newLine << newLine; @endcode */ extern NewLine newLine; //============================================================================== /** Writes a new-line sequence to a string. You can use the predefined object 'newLine' to invoke this, e.g. @code myString << "Hello World" << newLine << newLine; @endcode */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const NewLine&); #if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) inline String operator+ (String s1, const NewLine&) { return s1 += NewLine::getDefault(); } #endif #endif // JUCE_NEWLINE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_String.cpp000066400000000000000000002642531320201440200275220ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4996) #endif NewLine newLine; #if defined (JUCE_STRINGS_ARE_UNICODE) && ! JUCE_STRINGS_ARE_UNICODE #error "JUCE_STRINGS_ARE_UNICODE is deprecated! All strings are now unicode by default." #endif #if JUCE_NATIVE_WCHAR_IS_UTF8 typedef CharPointer_UTF8 CharPointer_wchar_t; #elif JUCE_NATIVE_WCHAR_IS_UTF16 typedef CharPointer_UTF16 CharPointer_wchar_t; #else typedef CharPointer_UTF32 CharPointer_wchar_t; #endif static inline CharPointer_wchar_t castToCharPointer_wchar_t (const void* t) noexcept { return CharPointer_wchar_t (static_cast (t)); } //============================================================================== // (Mirrors the structure of StringHolder, but without the atomic member, so can be statically constructed) struct EmptyString { int refCount; size_t allocatedBytes; String::CharPointerType::CharType text; }; static const EmptyString emptyString = { 0x3fffffff, sizeof (String::CharPointerType::CharType), 0 }; //============================================================================== class StringHolder { public: StringHolder() JUCE_DELETED_FUNCTION; typedef String::CharPointerType CharPointerType; typedef String::CharPointerType::CharType CharType; //============================================================================== static CharPointerType createUninitialisedBytes (size_t numBytes) { numBytes = (numBytes + 3) & ~(size_t) 3; StringHolder* const s = reinterpret_cast (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]); s->refCount.value = 0; s->allocatedNumBytes = numBytes; return CharPointerType (s->text); } template static CharPointerType createFromCharPointer (const CharPointer text) { if (text.getAddress() == nullptr || text.isEmpty()) return CharPointerType (&(emptyString.text)); CharPointer t (text); size_t bytesNeeded = sizeof (CharType); while (! t.isEmpty()) bytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); const CharPointerType dest (createUninitialisedBytes (bytesNeeded)); CharPointerType (dest).writeAll (text); return dest; } template static CharPointerType createFromCharPointer (const CharPointer text, size_t maxChars) { if (text.getAddress() == nullptr || text.isEmpty() || maxChars == 0) return CharPointerType (&(emptyString.text)); CharPointer end (text); size_t numChars = 0; size_t bytesNeeded = sizeof (CharType); while (numChars < maxChars && ! end.isEmpty()) { bytesNeeded += CharPointerType::getBytesRequiredFor (end.getAndAdvance()); ++numChars; } const CharPointerType dest (createUninitialisedBytes (bytesNeeded)); CharPointerType (dest).writeWithCharLimit (text, (int) numChars + 1); return dest; } template static CharPointerType createFromCharPointer (const CharPointer start, const CharPointer end) { if (start.getAddress() == nullptr || start.isEmpty()) return CharPointerType (&(emptyString.text)); CharPointer e (start); int numChars = 0; size_t bytesNeeded = sizeof (CharType); while (e < end && ! e.isEmpty()) { bytesNeeded += CharPointerType::getBytesRequiredFor (e.getAndAdvance()); ++numChars; } const CharPointerType dest (createUninitialisedBytes (bytesNeeded)); CharPointerType (dest).writeWithCharLimit (start, numChars + 1); return dest; } static CharPointerType createFromCharPointer (const CharPointerType start, const CharPointerType end) { if (start.getAddress() == nullptr || start.isEmpty()) return CharPointerType (&(emptyString.text)); const size_t numBytes = (size_t) (reinterpret_cast (end.getAddress()) - reinterpret_cast (start.getAddress())); const CharPointerType dest (createUninitialisedBytes (numBytes + sizeof (CharType))); memcpy (dest.getAddress(), start, numBytes); dest.getAddress()[numBytes / sizeof (CharType)] = 0; return dest; } static CharPointerType createFromFixedLength (const char* const src, const size_t numChars) { const CharPointerType dest (createUninitialisedBytes (numChars * sizeof (CharType) + sizeof (CharType))); CharPointerType (dest).writeWithCharLimit (CharPointer_UTF8 (src), (int) (numChars + 1)); return dest; } //============================================================================== static void retain (const CharPointerType text) noexcept { StringHolder* const b = bufferFromText (text); if (b != (StringHolder*) &emptyString) ++(b->refCount); } static inline void release (StringHolder* const b) noexcept { if (b != (StringHolder*) &emptyString) if (--(b->refCount) == -1) delete[] reinterpret_cast (b); } static void release (const CharPointerType text) noexcept { release (bufferFromText (text)); } static inline int getReferenceCount (const CharPointerType text) noexcept { return bufferFromText (text)->refCount.get() + 1; } //============================================================================== static CharPointerType makeUniqueWithByteSize (const CharPointerType text, size_t numBytes) { StringHolder* const b = bufferFromText (text); if (b == (StringHolder*) &emptyString) { CharPointerType newText (createUninitialisedBytes (numBytes)); newText.writeNull(); return newText; } if (b->allocatedNumBytes >= numBytes && b->refCount.get() <= 0) return text; CharPointerType newText (createUninitialisedBytes (jmax (b->allocatedNumBytes, numBytes))); memcpy (newText.getAddress(), text.getAddress(), b->allocatedNumBytes); release (b); return newText; } static size_t getAllocatedNumBytes (const CharPointerType text) noexcept { return bufferFromText (text)->allocatedNumBytes; } //============================================================================== Atomic refCount; size_t allocatedNumBytes; CharType text[1]; private: static inline StringHolder* bufferFromText (const CharPointerType text) noexcept { // (Can't use offsetof() here because of warnings about this not being a POD) return reinterpret_cast (reinterpret_cast (text.getAddress()) - (reinterpret_cast (reinterpret_cast (1)->text) - 1)); } void compileTimeChecks() { // Let me know if any of these assertions fail on your system! #if JUCE_NATIVE_WCHAR_IS_UTF8 static_jassert (sizeof (wchar_t) == 1); #elif JUCE_NATIVE_WCHAR_IS_UTF16 static_jassert (sizeof (wchar_t) == 2); #elif JUCE_NATIVE_WCHAR_IS_UTF32 static_jassert (sizeof (wchar_t) == 4); #else #error "native wchar_t size is unknown" #endif static_jassert (sizeof (EmptyString) == sizeof (StringHolder)); } }; const String String::empty; //============================================================================== String::String() noexcept : text (&(emptyString.text)) { } String::~String() noexcept { StringHolder::release (text); } String::String (const String& other) noexcept : text (other.text) { StringHolder::retain (text); } void String::swapWith (String& other) noexcept { std::swap (text, other.text); } void String::clear() noexcept { StringHolder::release (text); text = &(emptyString.text); } String& String::operator= (const String& other) noexcept { StringHolder::retain (other.text); StringHolder::release (text.atomicSwap (other.text)); return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS String::String (String&& other) noexcept : text (other.text) { other.text = &(emptyString.text); } String& String::operator= (String&& other) noexcept { std::swap (text, other.text); return *this; } #endif inline String::PreallocationBytes::PreallocationBytes (const size_t num) noexcept : numBytes (num) {} String::String (const PreallocationBytes& preallocationSize) : text (StringHolder::createUninitialisedBytes (preallocationSize.numBytes + sizeof (CharPointerType::CharType))) { } void String::preallocateBytes (const size_t numBytesNeeded) { text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); } int String::getReferenceCount() const noexcept { return StringHolder::getReferenceCount (text); } //============================================================================== String::String (const char* const t) : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t))) { /* If you get an assertion here, then you're trying to create a string from 8-bit data that contains values greater than 127. These can NOT be correctly converted to unicode because there's no way for the String class to know what encoding was used to create them. The source data could be UTF-8, ASCII or one of many local code-pages. To get around this problem, you must be more explicit when you pass an ambiguous 8-bit string to the String class - so for example if your source data is actually UTF-8, you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to correctly convert the multi-byte characters to unicode. It's *highly* recommended that you use UTF-8 with escape characters in your source code to represent extended characters, because there's no other way to represent these strings in a way that isn't dependent on the compiler, source code editor and platform. Note that the Introjucer has a handy string literal generator utility that will convert any unicode string to a valid C++ string literal, creating ascii escape sequences that will work in any compiler. */ jassert (t == nullptr || CharPointer_ASCII::isValidString (t, std::numeric_limits::max())); } String::String (const char* const t, const size_t maxChars) : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t), maxChars)) { /* If you get an assertion here, then you're trying to create a string from 8-bit data that contains values greater than 127. These can NOT be correctly converted to unicode because there's no way for the String class to know what encoding was used to create them. The source data could be UTF-8, ASCII or one of many local code-pages. To get around this problem, you must be more explicit when you pass an ambiguous 8-bit string to the String class - so for example if your source data is actually UTF-8, you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to correctly convert the multi-byte characters to unicode. It's *highly* recommended that you use UTF-8 with escape characters in your source code to represent extended characters, because there's no other way to represent these strings in a way that isn't dependent on the compiler, source code editor and platform. Note that the Introjucer has a handy string literal generator utility that will convert any unicode string to a valid C++ string literal, creating ascii escape sequences that will work in any compiler. */ jassert (t == nullptr || CharPointer_ASCII::isValidString (t, (int) maxChars)); } String::String (const wchar_t* const t) : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t))) {} String::String (const CharPointer_UTF8 t) : text (StringHolder::createFromCharPointer (t)) {} String::String (const CharPointer_UTF16 t) : text (StringHolder::createFromCharPointer (t)) {} String::String (const CharPointer_UTF32 t) : text (StringHolder::createFromCharPointer (t)) {} String::String (const CharPointer_ASCII t) : text (StringHolder::createFromCharPointer (t)) {} String::String (const CharPointer_UTF8 t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} String::String (const CharPointer_UTF16 t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} String::String (const CharPointer_UTF32 t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} String::String (const wchar_t* const t, size_t maxChars) : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t), maxChars)) {} String::String (const CharPointer_UTF8 start, const CharPointer_UTF8 end) : text (StringHolder::createFromCharPointer (start, end)) {} String::String (const CharPointer_UTF16 start, const CharPointer_UTF16 end) : text (StringHolder::createFromCharPointer (start, end)) {} String::String (const CharPointer_UTF32 start, const CharPointer_UTF32 end) : text (StringHolder::createFromCharPointer (start, end)) {} String::String (const std::string& s) : text (StringHolder::createFromFixedLength (s.data(), s.size())) {} String String::charToString (const juce_wchar character) { String result (PreallocationBytes (CharPointerType::getBytesRequiredFor (character))); CharPointerType t (result.text); t.write (character); t.writeNull(); return result; } //============================================================================== namespace NumberToStringConverters { enum { charsNeededForInt = 32, charsNeededForDouble = 48 }; template static char* printDigits (char* t, Type v) noexcept { *--t = 0; do { *--t = '0' + (char) (v % 10); v /= 10; } while (v > 0); return t; } // pass in a pointer to the END of a buffer.. static char* numberToString (char* t, const int64 n) noexcept { if (n >= 0) return printDigits (t, static_cast (n)); // NB: this needs to be careful not to call -std::numeric_limits::min(), // which has undefined behaviour t = printDigits (t, static_cast (-(n + 1)) + 1); *--t = '-'; return t; } static char* numberToString (char* t, uint64 v) noexcept { return printDigits (t, v); } static char* numberToString (char* t, const int n) noexcept { if (n >= 0) return printDigits (t, static_cast (n)); // NB: this needs to be careful not to call -std::numeric_limits::min(), // which has undefined behaviour t = printDigits (t, static_cast (-(n + 1)) + 1); *--t = '-'; return t; } static char* numberToString (char* t, unsigned int v) noexcept { return printDigits (t, v); } struct StackArrayStream : public std::basic_streambuf > { explicit StackArrayStream (char* d) { static const std::locale classicLocale (std::locale::classic()); imbue (classicLocale); setp (d, d + charsNeededForDouble); } size_t writeDouble (double n, int numDecPlaces) { { std::ostream o (this); if (numDecPlaces > 0) o.precision ((std::streamsize) numDecPlaces); o << n; } return (size_t) (pptr() - pbase()); } }; static char* doubleToString (char* buffer, const int numChars, double n, int numDecPlaces, size_t& len) noexcept { if (numDecPlaces > 0 && numDecPlaces < 7 && n > -1.0e20 && n < 1.0e20) { char* const end = buffer + numChars; char* t = end; int64 v = (int64) (pow (10.0, numDecPlaces) * std::abs (n) + 0.5); *--t = (char) 0; while (numDecPlaces >= 0 || v > 0) { if (numDecPlaces == 0) *--t = '.'; *--t = (char) ('0' + (v % 10)); v /= 10; --numDecPlaces; } if (n < 0) *--t = '-'; len = (size_t) (end - t - 1); return t; } StackArrayStream strm (buffer); len = strm.writeDouble (n, numDecPlaces); jassert (len <= charsNeededForDouble); return buffer; } template static String::CharPointerType createFromInteger (const IntegerType number) { char buffer [charsNeededForInt]; char* const end = buffer + numElementsInArray (buffer); char* const start = numberToString (end, number); return StringHolder::createFromFixedLength (start, (size_t) (end - start - 1)); } static String::CharPointerType createFromDouble (const double number, const int numberOfDecimalPlaces) { char buffer [charsNeededForDouble]; size_t len; char* const start = doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); return StringHolder::createFromFixedLength (start, len); } } //============================================================================== String::String (const int number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const unsigned int number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const short number) : text (NumberToStringConverters::createFromInteger ((int) number)) {} String::String (const unsigned short number) : text (NumberToStringConverters::createFromInteger ((unsigned int) number)) {} String::String (const int64 number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const uint64 number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const float number) : text (NumberToStringConverters::createFromDouble ((double) number, 0)) {} String::String (const double number) : text (NumberToStringConverters::createFromDouble (number, 0)) {} String::String (const float number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble ((double) number, numberOfDecimalPlaces)) {} String::String (const double number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble (number, numberOfDecimalPlaces)) {} //============================================================================== int String::length() const noexcept { return (int) text.length(); } static size_t findByteOffsetOfEnd (String::CharPointerType text) noexcept { return (size_t) (((char*) text.findTerminatingNull().getAddress()) - (char*) text.getAddress()); } size_t String::getByteOffsetOfEnd() const noexcept { return findByteOffsetOfEnd (text); } juce_wchar String::operator[] (int index) const noexcept { jassert (index == 0 || (index > 0 && index <= (int) text.lengthUpTo ((size_t) index + 1))); return text [index]; } template struct HashGenerator { template static Type calculate (CharPointer t) noexcept { Type result = Type(); while (! t.isEmpty()) result = ((Type) multiplier) * result + (Type) t.getAndAdvance(); return result; } enum { multiplier = sizeof (Type) > 4 ? 101 : 31 }; }; int String::hashCode() const noexcept { return HashGenerator ::calculate (text); } int64 String::hashCode64() const noexcept { return HashGenerator ::calculate (text); } size_t String::hash() const noexcept { return HashGenerator ::calculate (text); } //============================================================================== JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const String& s2) noexcept { return s1.compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const String& s2) noexcept { return s1.compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const char* s2) noexcept { return s1.compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const char* s2) noexcept { return s1.compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* s2) noexcept { return s1.compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* s2) noexcept { return s1.compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator> (const String& s1, const String& s2) noexcept { return s1.compare (s2) > 0; } JUCE_API bool JUCE_CALLTYPE operator< (const String& s1, const String& s2) noexcept { return s1.compare (s2) < 0; } JUCE_API bool JUCE_CALLTYPE operator>= (const String& s1, const String& s2) noexcept { return s1.compare (s2) >= 0; } JUCE_API bool JUCE_CALLTYPE operator<= (const String& s1, const String& s2) noexcept { return s1.compare (s2) <= 0; } bool String::equalsIgnoreCase (const wchar_t* const t) const noexcept { return t != nullptr ? text.compareIgnoreCase (castToCharPointer_wchar_t (t)) == 0 : isEmpty(); } bool String::equalsIgnoreCase (const char* const t) const noexcept { return t != nullptr ? text.compareIgnoreCase (CharPointer_UTF8 (t)) == 0 : isEmpty(); } bool String::equalsIgnoreCase (StringRef t) const noexcept { return text.compareIgnoreCase (t.text) == 0; } bool String::equalsIgnoreCase (const String& other) const noexcept { return text == other.text || text.compareIgnoreCase (other.text) == 0; } int String::compare (const String& other) const noexcept { return (text == other.text) ? 0 : text.compare (other.text); } int String::compare (const char* const other) const noexcept { return text.compare (CharPointer_UTF8 (other)); } int String::compare (const wchar_t* const other) const noexcept { return text.compare (castToCharPointer_wchar_t (other)); } int String::compareIgnoreCase (const String& other) const noexcept { return (text == other.text) ? 0 : text.compareIgnoreCase (other.text); } static int stringCompareRight (String::CharPointerType s1, String::CharPointerType s2) noexcept { for (int bias = 0;;) { const juce_wchar c1 = s1.getAndAdvance(); const bool isDigit1 = CharacterFunctions::isDigit (c1); const juce_wchar c2 = s2.getAndAdvance(); const bool isDigit2 = CharacterFunctions::isDigit (c2); if (! (isDigit1 || isDigit2)) return bias; if (! isDigit1) return -1; if (! isDigit2) return 1; if (c1 != c2 && bias == 0) bias = c1 < c2 ? -1 : 1; jassert (c1 != 0 && c2 != 0); } } static int stringCompareLeft (String::CharPointerType s1, String::CharPointerType s2) noexcept { for (;;) { const juce_wchar c1 = s1.getAndAdvance(); const bool isDigit1 = CharacterFunctions::isDigit (c1); const juce_wchar c2 = s2.getAndAdvance(); const bool isDigit2 = CharacterFunctions::isDigit (c2); if (! (isDigit1 || isDigit2)) return 0; if (! isDigit1) return -1; if (! isDigit2) return 1; if (c1 < c2) return -1; if (c1 > c2) return 1; } } static int naturalStringCompare (String::CharPointerType s1, String::CharPointerType s2) noexcept { bool firstLoop = true; for (;;) { const bool hasSpace1 = s1.isWhitespace(); const bool hasSpace2 = s2.isWhitespace(); if ((! firstLoop) && (hasSpace1 ^ hasSpace2)) return hasSpace2 ? 1 : -1; firstLoop = false; if (hasSpace1) s1 = s1.findEndOfWhitespace(); if (hasSpace2) s2 = s2.findEndOfWhitespace(); if (s1.isDigit() && s2.isDigit()) { const int result = (*s1 == '0' || *s2 == '0') ? stringCompareLeft (s1, s2) : stringCompareRight (s1, s2); if (result != 0) return result; } juce_wchar c1 = s1.getAndAdvance(); juce_wchar c2 = s2.getAndAdvance(); if (c1 != c2) { c1 = CharacterFunctions::toUpperCase (c1); c2 = CharacterFunctions::toUpperCase (c2); } if (c1 == c2) { if (c1 == 0) return 0; } else { const bool isAlphaNum1 = CharacterFunctions::isLetterOrDigit (c1); const bool isAlphaNum2 = CharacterFunctions::isLetterOrDigit (c2); if (isAlphaNum2 && ! isAlphaNum1) return -1; if (isAlphaNum1 && ! isAlphaNum2) return 1; return c1 < c2 ? -1 : 1; } jassert (c1 != 0 && c2 != 0); } } int String::compareNatural (StringRef other) const noexcept { return naturalStringCompare (getCharPointer(), other.text); } //============================================================================== void String::append (const String& textToAppend, size_t maxCharsToTake) { appendCharPointer (this == &textToAppend ? String (textToAppend).text : textToAppend.text, maxCharsToTake); } void String::appendCharPointer (const CharPointerType textToAppend) { appendCharPointer (textToAppend, textToAppend.findTerminatingNull()); } void String::appendCharPointer (const CharPointerType startOfTextToAppend, const CharPointerType endOfTextToAppend) { jassert (startOfTextToAppend.getAddress() != nullptr && endOfTextToAppend.getAddress() != nullptr); const int extraBytesNeeded = getAddressDifference (endOfTextToAppend.getAddress(), startOfTextToAppend.getAddress()); jassert (extraBytesNeeded >= 0); if (extraBytesNeeded > 0) { const size_t byteOffsetOfNull = getByteOffsetOfEnd(); preallocateBytes (byteOffsetOfNull + (size_t) extraBytesNeeded); CharPointerType::CharType* const newStringStart = addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull); memcpy (newStringStart, startOfTextToAppend.getAddress(), (size_t) extraBytesNeeded); CharPointerType (addBytesToPointer (newStringStart, extraBytesNeeded)).writeNull(); } } String& String::operator+= (const wchar_t* const t) { appendCharPointer (castToCharPointer_wchar_t (t)); return *this; } String& String::operator+= (const char* const t) { appendCharPointer (CharPointer_UTF8 (t)); // (using UTF8 here triggers a faster code-path than ascii) return *this; } String& String::operator+= (const String& other) { if (isEmpty()) return operator= (other); if (this == &other) return operator+= (String (*this)); appendCharPointer (other.text); return *this; } String& String::operator+= (const char ch) { const char asString[] = { ch, 0 }; return operator+= (asString); } String& String::operator+= (const wchar_t ch) { const wchar_t asString[] = { ch, 0 }; return operator+= (asString); } #if ! JUCE_NATIVE_WCHAR_IS_UTF32 String& String::operator+= (const juce_wchar ch) { const juce_wchar asString[] = { ch, 0 }; appendCharPointer (CharPointer_UTF32 (asString)); return *this; } #endif String& String::operator+= (const int number) { char buffer [16]; char* end = buffer + numElementsInArray (buffer); char* start = NumberToStringConverters::numberToString (end, number); #if (JUCE_STRING_UTF_TYPE == 8) appendCharPointer (CharPointerType (start), CharPointerType (end)); #else appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end)); #endif return *this; } String& String::operator+= (int64 number) { char buffer [32]; char* end = buffer + numElementsInArray (buffer); char* start = NumberToStringConverters::numberToString (end, number); #if (JUCE_STRING_UTF_TYPE == 8) appendCharPointer (CharPointerType (start), CharPointerType (end)); #else appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end)); #endif return *this; } //============================================================================== JUCE_API String JUCE_CALLTYPE operator+ (const char* const s1, const String& s2) { String s (s1); return s += s2; } JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t* const s1, const String& s2) { String s (s1); return s += s2; } JUCE_API String JUCE_CALLTYPE operator+ (const char s1, const String& s2) { return String::charToString ((juce_wchar) (uint8) s1) + s2; } JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t s1, const String& s2) { return String::charToString (s1) + s2; } JUCE_API String JUCE_CALLTYPE operator+ (String s1, const String& s2) { return s1 += s2; } JUCE_API String JUCE_CALLTYPE operator+ (String s1, const char* const s2) { return s1 += s2; } JUCE_API String JUCE_CALLTYPE operator+ (String s1, const wchar_t* s2) { return s1 += s2; } JUCE_API String JUCE_CALLTYPE operator+ (String s1, const char s2) { return s1 += s2; } JUCE_API String JUCE_CALLTYPE operator+ (String s1, const wchar_t s2) { return s1 += s2; } #if ! JUCE_NATIVE_WCHAR_IS_UTF32 JUCE_API String JUCE_CALLTYPE operator+ (const juce_wchar s1, const String& s2) { return String::charToString (s1) + s2; } JUCE_API String JUCE_CALLTYPE operator+ (String s1, const juce_wchar s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const juce_wchar s2) { return s1 += s2; } #endif JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char* const s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t* const s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const String& s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int number) { return s1 += number; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const short number) { return s1 += (int) number; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const long number) { return s1 += (int) number; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int64 number) { return s1 += String (number); } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const float number) { return s1 += String (number); } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const double number) { return s1 += String (number); } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const uint64 number) { return s1 += String (number); } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text) { return operator<< (stream, StringRef (text)); } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef text) { const size_t numBytes = CharPointer_UTF8::getBytesRequiredFor (text.text); #if (JUCE_STRING_UTF_TYPE == 8) stream.write (text.text.getAddress(), numBytes); #else // (This avoids using toUTF8() to prevent the memory bloat that it would leave behind // if lots of large, persistent strings were to be written to streams). HeapBlock temp (numBytes + 1); CharPointer_UTF8 (temp).writeAll (text.text); stream.write (temp, numBytes); #endif return stream; } JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const NewLine&) { return string1 += NewLine::getDefault(); } //============================================================================== int String::indexOfChar (const juce_wchar character) const noexcept { return text.indexOf (character); } int String::indexOfChar (const int startIndex, const juce_wchar character) const noexcept { CharPointerType t (text); for (int i = 0; ! t.isEmpty(); ++i) { if (i >= startIndex) { if (t.getAndAdvance() == character) return i; } else { ++t; } } return -1; } int String::lastIndexOfChar (const juce_wchar character) const noexcept { CharPointerType t (text); int last = -1; for (int i = 0; ! t.isEmpty(); ++i) if (t.getAndAdvance() == character) last = i; return last; } int String::indexOfAnyOf (StringRef charactersToLookFor, const int startIndex, const bool ignoreCase) const noexcept { CharPointerType t (text); for (int i = 0; ! t.isEmpty(); ++i) { if (i >= startIndex) { if (charactersToLookFor.text.indexOf (t.getAndAdvance(), ignoreCase) >= 0) return i; } else { ++t; } } return -1; } int String::indexOf (StringRef other) const noexcept { return other.isEmpty() ? 0 : text.indexOf (other.text); } int String::indexOfIgnoreCase (StringRef other) const noexcept { return other.isEmpty() ? 0 : CharacterFunctions::indexOfIgnoreCase (text, other.text); } int String::indexOf (const int startIndex, StringRef other) const noexcept { if (other.isEmpty()) return -1; CharPointerType t (text); for (int i = startIndex; --i >= 0;) { if (t.isEmpty()) return -1; ++t; } int found = t.indexOf (other.text); if (found >= 0) found += startIndex; return found; } int String::indexOfIgnoreCase (const int startIndex, StringRef other) const noexcept { if (other.isEmpty()) return -1; CharPointerType t (text); for (int i = startIndex; --i >= 0;) { if (t.isEmpty()) return -1; ++t; } int found = CharacterFunctions::indexOfIgnoreCase (t, other.text); if (found >= 0) found += startIndex; return found; } int String::lastIndexOf (StringRef other) const noexcept { if (other.isNotEmpty()) { const int len = other.length(); int i = length() - len; if (i >= 0) { for (CharPointerType n (text + i); i >= 0; --i) { if (n.compareUpTo (other.text, len) == 0) return i; --n; } } } return -1; } int String::lastIndexOfIgnoreCase (StringRef other) const noexcept { if (other.isNotEmpty()) { const int len = other.length(); int i = length() - len; if (i >= 0) { for (CharPointerType n (text + i); i >= 0; --i) { if (n.compareIgnoreCaseUpTo (other.text, len) == 0) return i; --n; } } } return -1; } int String::lastIndexOfAnyOf (StringRef charactersToLookFor, const bool ignoreCase) const noexcept { CharPointerType t (text); int last = -1; for (int i = 0; ! t.isEmpty(); ++i) if (charactersToLookFor.text.indexOf (t.getAndAdvance(), ignoreCase) >= 0) last = i; return last; } bool String::contains (StringRef other) const noexcept { return indexOf (other) >= 0; } bool String::containsChar (const juce_wchar character) const noexcept { return text.indexOf (character) >= 0; } bool String::containsIgnoreCase (StringRef t) const noexcept { return indexOfIgnoreCase (t) >= 0; } int String::indexOfWholeWord (StringRef word) const noexcept { if (word.isNotEmpty()) { CharPointerType t (text); const int wordLen = word.length(); const int end = (int) t.length() - wordLen; for (int i = 0; i <= end; ++i) { if (t.compareUpTo (word.text, wordLen) == 0 && (i == 0 || ! (t - 1).isLetterOrDigit()) && ! (t + wordLen).isLetterOrDigit()) return i; ++t; } } return -1; } int String::indexOfWholeWordIgnoreCase (StringRef word) const noexcept { if (word.isNotEmpty()) { CharPointerType t (text); const int wordLen = word.length(); const int end = (int) t.length() - wordLen; for (int i = 0; i <= end; ++i) { if (t.compareIgnoreCaseUpTo (word.text, wordLen) == 0 && (i == 0 || ! (t - 1).isLetterOrDigit()) && ! (t + wordLen).isLetterOrDigit()) return i; ++t; } } return -1; } bool String::containsWholeWord (StringRef wordToLookFor) const noexcept { return indexOfWholeWord (wordToLookFor) >= 0; } bool String::containsWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept { return indexOfWholeWordIgnoreCase (wordToLookFor) >= 0; } //============================================================================== template struct WildCardMatcher { static bool matches (CharPointer wildcard, CharPointer test, const bool ignoreCase) noexcept { for (;;) { const juce_wchar wc = wildcard.getAndAdvance(); if (wc == '*') return wildcard.isEmpty() || matchesAnywhere (wildcard, test, ignoreCase); if (! characterMatches (wc, test.getAndAdvance(), ignoreCase)) return false; if (wc == 0) return true; } } static bool characterMatches (const juce_wchar wc, const juce_wchar tc, const bool ignoreCase) noexcept { return (wc == tc) || (wc == '?' && tc != 0) || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (tc)); } static bool matchesAnywhere (const CharPointer wildcard, CharPointer test, const bool ignoreCase) noexcept { for (; ! test.isEmpty(); ++test) if (matches (wildcard, test, ignoreCase)) return true; return false; } }; bool String::matchesWildcard (StringRef wildcard, const bool ignoreCase) const noexcept { return WildCardMatcher::matches (wildcard.text, text, ignoreCase); } //============================================================================== String String::repeatedString (StringRef stringToRepeat, int numberOfTimesToRepeat) { if (numberOfTimesToRepeat <= 0) return String(); String result (PreallocationBytes (findByteOffsetOfEnd (stringToRepeat) * (size_t) numberOfTimesToRepeat)); CharPointerType n (result.text); while (--numberOfTimesToRepeat >= 0) n.writeAll (stringToRepeat.text); return result; } String String::paddedLeft (const juce_wchar padCharacter, int minimumLength) const { jassert (padCharacter != 0); int extraChars = minimumLength; CharPointerType end (text); while (! end.isEmpty()) { --extraChars; ++end; } if (extraChars <= 0 || padCharacter == 0) return *this; const size_t currentByteSize = (size_t) (((char*) end.getAddress()) - (char*) text.getAddress()); String result (PreallocationBytes (currentByteSize + (size_t) extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); CharPointerType n (result.text); while (--extraChars >= 0) n.write (padCharacter); n.writeAll (text); return result; } String String::paddedRight (const juce_wchar padCharacter, int minimumLength) const { jassert (padCharacter != 0); int extraChars = minimumLength; CharPointerType end (text); while (! end.isEmpty()) { --extraChars; ++end; } if (extraChars <= 0 || padCharacter == 0) return *this; const size_t currentByteSize = (size_t) (((char*) end.getAddress()) - (char*) text.getAddress()); String result (PreallocationBytes (currentByteSize + (size_t) extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); CharPointerType n (result.text); n.writeAll (text); while (--extraChars >= 0) n.write (padCharacter); n.writeNull(); return result; } //============================================================================== String String::replaceSection (int index, int numCharsToReplace, StringRef stringToInsert) const { if (index < 0) { // a negative index to replace from? jassertfalse; index = 0; } if (numCharsToReplace < 0) { // replacing a negative number of characters? numCharsToReplace = 0; jassertfalse; } CharPointerType insertPoint (text); for (int i = 0; i < index; ++i) { if (insertPoint.isEmpty()) { // replacing beyond the end of the string? jassertfalse; return *this + stringToInsert; } ++insertPoint; } CharPointerType startOfRemainder (insertPoint); for (int i = 0; i < numCharsToReplace && ! startOfRemainder.isEmpty(); ++i) ++startOfRemainder; if (insertPoint == text && startOfRemainder.isEmpty()) return stringToInsert.text; const size_t initialBytes = (size_t) (((char*) insertPoint.getAddress()) - (char*) text.getAddress()); const size_t newStringBytes = findByteOffsetOfEnd (stringToInsert); const size_t remainderBytes = (size_t) (((char*) startOfRemainder.findTerminatingNull().getAddress()) - (char*) startOfRemainder.getAddress()); const size_t newTotalBytes = initialBytes + newStringBytes + remainderBytes; if (newTotalBytes <= 0) return String(); String result (PreallocationBytes ((size_t) newTotalBytes)); char* dest = (char*) result.text.getAddress(); memcpy (dest, text.getAddress(), initialBytes); dest += initialBytes; memcpy (dest, stringToInsert.text.getAddress(), newStringBytes); dest += newStringBytes; memcpy (dest, startOfRemainder.getAddress(), remainderBytes); dest += remainderBytes; CharPointerType ((CharPointerType::CharType*) dest).writeNull(); return result; } String String::replace (StringRef stringToReplace, StringRef stringToInsert, const bool ignoreCase) const { const int stringToReplaceLen = stringToReplace.length(); const int stringToInsertLen = stringToInsert.length(); int i = 0; String result (*this); while ((i = (ignoreCase ? result.indexOfIgnoreCase (i, stringToReplace) : result.indexOf (i, stringToReplace))) >= 0) { result = result.replaceSection (i, stringToReplaceLen, stringToInsert); i += stringToInsertLen; } return result; } class StringCreationHelper { public: StringCreationHelper (const size_t initialBytes) : source (nullptr), dest (nullptr), allocatedBytes (initialBytes), bytesWritten (0) { result.preallocateBytes (allocatedBytes); dest = result.getCharPointer(); } StringCreationHelper (const String::CharPointerType s) : source (s), dest (nullptr), allocatedBytes (StringHolder::getAllocatedNumBytes (s)), bytesWritten (0) { result.preallocateBytes (allocatedBytes); dest = result.getCharPointer(); } void write (juce_wchar c) { bytesWritten += String::CharPointerType::getBytesRequiredFor (c); if (bytesWritten > allocatedBytes) { allocatedBytes += jmax ((size_t) 8, allocatedBytes / 16); const size_t destOffset = (size_t) (((char*) dest.getAddress()) - (char*) result.getCharPointer().getAddress()); result.preallocateBytes (allocatedBytes); dest = addBytesToPointer (result.getCharPointer().getAddress(), (int) destOffset); } dest.write (c); } String result; String::CharPointerType source; private: String::CharPointerType dest; size_t allocatedBytes, bytesWritten; }; String String::replaceCharacter (const juce_wchar charToReplace, const juce_wchar charToInsert) const { if (! containsChar (charToReplace)) return *this; StringCreationHelper builder (text); for (;;) { juce_wchar c = builder.source.getAndAdvance(); if (c == charToReplace) c = charToInsert; builder.write (c); if (c == 0) break; } return builder.result; } String String::replaceCharacters (StringRef charactersToReplace, StringRef charactersToInsertInstead) const { StringCreationHelper builder (text); for (;;) { juce_wchar c = builder.source.getAndAdvance(); const int index = charactersToReplace.text.indexOf (c); if (index >= 0) c = charactersToInsertInstead [index]; builder.write (c); if (c == 0) break; } return builder.result; } //============================================================================== bool String::startsWith (StringRef other) const noexcept { return text.compareUpTo (other.text, other.length()) == 0; } bool String::startsWithIgnoreCase (StringRef other) const noexcept { return text.compareIgnoreCaseUpTo (other.text, other.length()) == 0; } bool String::startsWithChar (const juce_wchar character) const noexcept { jassert (character != 0); // strings can't contain a null character! return *text == character; } bool String::endsWithChar (const juce_wchar character) const noexcept { jassert (character != 0); // strings can't contain a null character! if (text.isEmpty()) return false; CharPointerType t (text.findTerminatingNull()); return *--t == character; } bool String::endsWith (StringRef other) const noexcept { CharPointerType end (text.findTerminatingNull()); CharPointerType otherEnd (other.text.findTerminatingNull()); while (end > text && otherEnd > other.text) { --end; --otherEnd; if (*end != *otherEnd) return false; } return otherEnd == other.text; } bool String::endsWithIgnoreCase (StringRef other) const noexcept { CharPointerType end (text.findTerminatingNull()); CharPointerType otherEnd (other.text.findTerminatingNull()); while (end > text && otherEnd > other.text) { --end; --otherEnd; if (end.toLowerCase() != otherEnd.toLowerCase()) return false; } return otherEnd == other.text; } //============================================================================== String String::toUpperCase() const { StringCreationHelper builder (text); for (;;) { const juce_wchar c = builder.source.toUpperCase(); builder.write (c); if (c == 0) break; ++(builder.source); } return builder.result; } String String::toLowerCase() const { StringCreationHelper builder (text); for (;;) { const juce_wchar c = builder.source.toLowerCase(); builder.write (c); if (c == 0) break; ++(builder.source); } return builder.result; } //============================================================================== juce_wchar String::getLastCharacter() const noexcept { return isEmpty() ? juce_wchar() : text [length() - 1]; } String String::substring (int start, const int end) const { if (start < 0) start = 0; if (end <= start) return String(); int i = 0; CharPointerType t1 (text); while (i < start) { if (t1.isEmpty()) return String(); ++i; ++t1; } CharPointerType t2 (t1); while (i < end) { if (t2.isEmpty()) { if (start == 0) return *this; break; } ++i; ++t2; } return String (t1, t2); } String String::substring (int start) const { if (start <= 0) return *this; CharPointerType t (text); while (--start >= 0) { if (t.isEmpty()) return String(); ++t; } return String (t); } String String::dropLastCharacters (const int numberToDrop) const { return String (text, (size_t) jmax (0, length() - numberToDrop)); } String String::getLastCharacters (const int numCharacters) const { return String (text + jmax (0, length() - jmax (0, numCharacters))); } String String::fromFirstOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? indexOfIgnoreCase (sub) : indexOf (sub); if (i < 0) return String(); return substring (includeSubString ? i : i + sub.length()); } String String::fromLastOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? lastIndexOfIgnoreCase (sub) : lastIndexOf (sub); if (i < 0) return *this; return substring (includeSubString ? i : i + sub.length()); } String String::upToFirstOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? indexOfIgnoreCase (sub) : indexOf (sub); if (i < 0) return *this; return substring (0, includeSubString ? i + sub.length() : i); } String String::upToLastOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? lastIndexOfIgnoreCase (sub) : lastIndexOf (sub); if (i < 0) return *this; return substring (0, includeSubString ? i + sub.length() : i); } bool String::isQuotedString() const { const juce_wchar trimmedStart = trimStart()[0]; return trimmedStart == '"' || trimmedStart == '\''; } String String::unquoted() const { const int len = length(); if (len == 0) return String(); const juce_wchar lastChar = text [len - 1]; const int dropAtStart = (*text == '"' || *text == '\'') ? 1 : 0; const int dropAtEnd = (lastChar == '"' || lastChar == '\'') ? 1 : 0; return substring (dropAtStart, len - dropAtEnd); } String String::quoted (const juce_wchar quoteCharacter) const { if (isEmpty()) return charToString (quoteCharacter) + quoteCharacter; String t (*this); if (! t.startsWithChar (quoteCharacter)) t = charToString (quoteCharacter) + t; if (! t.endsWithChar (quoteCharacter)) t += quoteCharacter; return t; } //============================================================================== static String::CharPointerType findTrimmedEnd (const String::CharPointerType start, String::CharPointerType end) { while (end > start) { if (! (--end).isWhitespace()) { ++end; break; } } return end; } String String::trim() const { if (isNotEmpty()) { CharPointerType start (text.findEndOfWhitespace()); const CharPointerType end (start.findTerminatingNull()); CharPointerType trimmedEnd (findTrimmedEnd (start, end)); if (trimmedEnd <= start) return String(); if (text < start || trimmedEnd < end) return String (start, trimmedEnd); } return *this; } String String::trimStart() const { if (isNotEmpty()) { const CharPointerType t (text.findEndOfWhitespace()); if (t != text) return String (t); } return *this; } String String::trimEnd() const { if (isNotEmpty()) { const CharPointerType end (text.findTerminatingNull()); CharPointerType trimmedEnd (findTrimmedEnd (text, end)); if (trimmedEnd < end) return String (text, trimmedEnd); } return *this; } String String::trimCharactersAtStart (StringRef charactersToTrim) const { CharPointerType t (text); while (charactersToTrim.text.indexOf (*t) >= 0) ++t; return t == text ? *this : String (t); } String String::trimCharactersAtEnd (StringRef charactersToTrim) const { if (isNotEmpty()) { const CharPointerType end (text.findTerminatingNull()); CharPointerType trimmedEnd (end); while (trimmedEnd > text) { if (charactersToTrim.text.indexOf (*--trimmedEnd) < 0) { ++trimmedEnd; break; } } if (trimmedEnd < end) return String (text, trimmedEnd); } return *this; } //============================================================================== String String::retainCharacters (StringRef charactersToRetain) const { if (isEmpty()) return String(); StringCreationHelper builder (text); for (;;) { juce_wchar c = builder.source.getAndAdvance(); if (charactersToRetain.text.indexOf (c) >= 0) builder.write (c); if (c == 0) break; } builder.write (0); return builder.result; } String String::removeCharacters (StringRef charactersToRemove) const { if (isEmpty()) return String(); StringCreationHelper builder (text); for (;;) { juce_wchar c = builder.source.getAndAdvance(); if (charactersToRemove.text.indexOf (c) < 0) builder.write (c); if (c == 0) break; } return builder.result; } String String::initialSectionContainingOnly (StringRef permittedCharacters) const { for (CharPointerType t (text); ! t.isEmpty(); ++t) if (permittedCharacters.text.indexOf (*t) < 0) return String (text, t); return *this; } String String::initialSectionNotContaining (StringRef charactersToStopAt) const { for (CharPointerType t (text); ! t.isEmpty(); ++t) if (charactersToStopAt.text.indexOf (*t) >= 0) return String (text, t); return *this; } bool String::containsOnly (StringRef chars) const noexcept { for (CharPointerType t (text); ! t.isEmpty();) if (chars.text.indexOf (t.getAndAdvance()) < 0) return false; return true; } bool String::containsAnyOf (StringRef chars) const noexcept { for (CharPointerType t (text); ! t.isEmpty();) if (chars.text.indexOf (t.getAndAdvance()) >= 0) return true; return false; } bool String::containsNonWhitespaceChars() const noexcept { for (CharPointerType t (text); ! t.isEmpty(); ++t) if (! t.isWhitespace()) return true; return false; } // Note! The format parameter here MUST NOT be a reference, otherwise MS's va_start macro fails to work (but still compiles). String String::formatted (const String pf, ... ) { size_t bufferSize = 256; for (;;) { va_list args; va_start (args, pf); #if JUCE_WINDOWS HeapBlock temp (bufferSize); const int num = (int) _vsnwprintf (temp.getData(), bufferSize - 1, pf.toWideCharPointer(), args); #elif JUCE_ANDROID HeapBlock temp (bufferSize); const int num = (int) vsnprintf (temp.getData(), bufferSize - 1, pf.toUTF8(), args); #else HeapBlock temp (bufferSize); const int num = (int) vswprintf (temp.getData(), bufferSize - 1, pf.toWideCharPointer(), args); #endif va_end (args); if (num > 0) return String (temp); bufferSize += 256; if (num == 0 || bufferSize > 65536) // the upper limit is a sanity check to avoid situations where vprintf repeatedly break; // returns -1 because of an error rather than because it needs more space. } return String(); } //============================================================================== int String::getIntValue() const noexcept { return text.getIntValue32(); } int64 String::getLargeIntValue() const noexcept { return text.getIntValue64(); } float String::getFloatValue() const noexcept { return (float) getDoubleValue(); } double String::getDoubleValue() const noexcept { return text.getDoubleValue(); } int String::getTrailingIntValue() const noexcept { int n = 0; int mult = 1; CharPointerType t (text.findTerminatingNull()); while (--t >= text) { if (! t.isDigit()) { if (*t == '-') n = -n; break; } n += mult * (*t - '0'); mult *= 10; } return n; } static const char hexDigits[] = "0123456789abcdef"; template static String hexToString (Type v) { String::CharPointerType::CharType buffer[32]; String::CharPointerType::CharType* const end = buffer + numElementsInArray (buffer) - 1; String::CharPointerType::CharType* t = end; *t = 0; do { *--t = hexDigits [(int) (v & 15)]; v >>= 4; } while (v != 0); return String (String::CharPointerType (t), String::CharPointerType (end)); } String String::toHexString (int number) { return hexToString ((unsigned int) number); } String String::toHexString (int64 number) { return hexToString ((uint64) number); } String String::toHexString (short number) { return toHexString ((int) (unsigned short) number); } String String::toHexString (const void* const d, const int size, const int groupSize) { if (size <= 0) return String(); int numChars = (size * 2) + 2; if (groupSize > 0) numChars += size / groupSize; String s (PreallocationBytes (sizeof (CharPointerType::CharType) * (size_t) numChars)); const unsigned char* data = static_cast (d); CharPointerType dest (s.text); for (int i = 0; i < size; ++i) { const unsigned char nextByte = *data++; dest.write ((juce_wchar) hexDigits [nextByte >> 4]); dest.write ((juce_wchar) hexDigits [nextByte & 0xf]); if (groupSize > 0 && (i % groupSize) == (groupSize - 1) && i < (size - 1)) dest.write ((juce_wchar) ' '); } dest.writeNull(); return s; } int String::getHexValue32() const noexcept { return CharacterFunctions::HexParser ::parse (text); } int64 String::getHexValue64() const noexcept { return CharacterFunctions::HexParser::parse (text); } //============================================================================== String String::createStringFromData (const void* const unknownData, const int size) { const uint8* const data = static_cast (unknownData); if (size <= 0 || data == nullptr) return String(); if (size == 1) return charToString ((juce_wchar) data[0]); if (CharPointer_UTF16::isByteOrderMarkBigEndian (data) || CharPointer_UTF16::isByteOrderMarkLittleEndian (data)) { const int numChars = size / 2 - 1; StringCreationHelper builder ((size_t) numChars); const uint16* const src = (const uint16*) (data + 2); if (CharPointer_UTF16::isByteOrderMarkBigEndian (data)) { for (int i = 0; i < numChars; ++i) builder.write ((juce_wchar) ByteOrder::swapIfLittleEndian (src[i])); } else { for (int i = 0; i < numChars; ++i) builder.write ((juce_wchar) ByteOrder::swapIfBigEndian (src[i])); } builder.write (0); return builder.result; } const uint8* start = data; if (size >= 3 && CharPointer_UTF8::isByteOrderMark (data)) start += 3; return String (CharPointer_UTF8 ((const char*) start), CharPointer_UTF8 ((const char*) (data + size))); } //============================================================================== static const juce_wchar emptyChar = 0; template struct StringEncodingConverter { static CharPointerType_Dest convert (const String& s) { String& source = const_cast (s); typedef typename CharPointerType_Dest::CharType DestChar; if (source.isEmpty()) return CharPointerType_Dest (reinterpret_cast (&emptyChar)); CharPointerType_Src text (source.getCharPointer()); const size_t extraBytesNeeded = CharPointerType_Dest::getBytesRequiredFor (text) + sizeof (typename CharPointerType_Dest::CharType); const size_t endOffset = (text.sizeInBytes() + 3) & ~3u; // the new string must be word-aligned or many Windows // functions will fail to read it correctly! source.preallocateBytes (endOffset + extraBytesNeeded); text = source.getCharPointer(); void* const newSpace = addBytesToPointer (text.getAddress(), (int) endOffset); const CharPointerType_Dest extraSpace (static_cast (newSpace)); #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) const size_t bytesToClear = (size_t) jmin ((int) extraBytesNeeded, 4); zeromem (addBytesToPointer (newSpace, extraBytesNeeded - bytesToClear), bytesToClear); #endif CharPointerType_Dest (extraSpace).writeAll (text); return extraSpace; } }; template <> struct StringEncodingConverter { static CharPointer_UTF8 convert (const String& source) noexcept { return CharPointer_UTF8 ((CharPointer_UTF8::CharType*) source.getCharPointer().getAddress()); } }; template <> struct StringEncodingConverter { static CharPointer_UTF16 convert (const String& source) noexcept { return CharPointer_UTF16 ((CharPointer_UTF16::CharType*) source.getCharPointer().getAddress()); } }; template <> struct StringEncodingConverter { static CharPointer_UTF32 convert (const String& source) noexcept { return CharPointer_UTF32 ((CharPointer_UTF32::CharType*) source.getCharPointer().getAddress()); } }; CharPointer_UTF8 String::toUTF8() const { return StringEncodingConverter::convert (*this); } CharPointer_UTF16 String::toUTF16() const { return StringEncodingConverter::convert (*this); } CharPointer_UTF32 String::toUTF32() const { return StringEncodingConverter::convert (*this); } const char* String::toRawUTF8() const { return toUTF8().getAddress(); } const wchar_t* String::toWideCharPointer() const { return StringEncodingConverter::convert (*this).getAddress(); } std::string String::toStdString() const { return std::string (toRawUTF8()); } //============================================================================== template struct StringCopier { static size_t copyToBuffer (const CharPointerType_Src source, typename CharPointerType_Dest::CharType* const buffer, const size_t maxBufferSizeBytes) { jassert (((ssize_t) maxBufferSizeBytes) >= 0); // keep this value positive! if (buffer == nullptr) return CharPointerType_Dest::getBytesRequiredFor (source) + sizeof (typename CharPointerType_Dest::CharType); return CharPointerType_Dest (buffer).writeWithDestByteLimit (source, maxBufferSizeBytes); } }; size_t String::copyToUTF8 (CharPointer_UTF8::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } size_t String::copyToUTF16 (CharPointer_UTF16::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } size_t String::copyToUTF32 (CharPointer_UTF32::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } //============================================================================== size_t String::getNumBytesAsUTF8() const noexcept { return CharPointer_UTF8::getBytesRequiredFor (text); } String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) { if (buffer != nullptr) { if (bufferSizeBytes < 0) return String (CharPointer_UTF8 (buffer)); if (bufferSizeBytes > 0) { jassert (CharPointer_UTF8::isValidString (buffer, bufferSizeBytes)); return String (CharPointer_UTF8 (buffer), CharPointer_UTF8 (buffer + bufferSizeBytes)); } } return String(); } #if JUCE_MSVC #pragma warning (pop) #endif //============================================================================== StringRef::StringRef() noexcept : text ((const String::CharPointerType::CharType*) "\0\0\0") { } StringRef::StringRef (const char* stringLiteral) noexcept #if JUCE_STRING_UTF_TYPE != 8 : text (nullptr), stringCopy (stringLiteral) #else : text (stringLiteral) #endif { #if JUCE_STRING_UTF_TYPE != 8 text = stringCopy.getCharPointer(); #endif jassert (stringLiteral != nullptr); // This must be a valid string literal, not a null pointer!! #if JUCE_NATIVE_WCHAR_IS_UTF8 /* If you get an assertion here, then you're trying to create a string from 8-bit data that contains values greater than 127. These can NOT be correctly converted to unicode because there's no way for the String class to know what encoding was used to create them. The source data could be UTF-8, ASCII or one of many local code-pages. To get around this problem, you must be more explicit when you pass an ambiguous 8-bit string to the StringRef class - so for example if your source data is actually UTF-8, you'd call StringRef (CharPointer_UTF8 ("my utf8 string..")), and it would be able to correctly convert the multi-byte characters to unicode. It's *highly* recommended that you use UTF-8 with escape characters in your source code to represent extended characters, because there's no other way to represent these strings in a way that isn't dependent on the compiler, source code editor and platform. */ jassert (CharPointer_ASCII::isValidString (stringLiteral, std::numeric_limits::max())); #endif } StringRef::StringRef (String::CharPointerType stringLiteral) noexcept : text (stringLiteral) { jassert (stringLiteral.getAddress() != nullptr); // This must be a valid string literal, not a null pointer!! } StringRef::StringRef (const String& string) noexcept : text (string.getCharPointer()) {} //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class StringTests : public UnitTest { public: StringTests() : UnitTest ("String class") {} template struct TestUTFConversion { static void test (UnitTest& test, Random& r) { String s (createRandomWideCharString (r)); typename CharPointerType::CharType buffer [300]; memset (buffer, 0xff, sizeof (buffer)); CharPointerType (buffer).writeAll (s.toUTF32()); test.expectEquals (String (CharPointerType (buffer)), s); memset (buffer, 0xff, sizeof (buffer)); CharPointerType (buffer).writeAll (s.toUTF16()); test.expectEquals (String (CharPointerType (buffer)), s); memset (buffer, 0xff, sizeof (buffer)); CharPointerType (buffer).writeAll (s.toUTF8()); test.expectEquals (String (CharPointerType (buffer)), s); test.expect (CharPointerType::isValidString (buffer, (int) strlen ((const char*) buffer))); } }; static String createRandomWideCharString (Random& r) { juce_wchar buffer[50] = { 0 }; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { if (r.nextBool()) { do { buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); } while (! CharPointer_UTF16::canRepresent (buffer[i])); } else buffer[i] = (juce_wchar) (1 + r.nextInt (0xff)); } return CharPointer_UTF32 (buffer); } void runTest() { Random r = getRandom(); { beginTest ("Basics"); expect (String().length() == 0); expect (String() == String::empty); String s1, s2 ("abcd"); expect (s1.isEmpty() && ! s1.isNotEmpty()); expect (s2.isNotEmpty() && ! s2.isEmpty()); expect (s2.length() == 4); s1 = "abcd"; expect (s2 == s1 && s1 == s2); expect (s1 == "abcd" && s1 == L"abcd"); expect (String ("abcd") == String (L"abcd")); expect (String ("abcdefg", 4) == L"abcd"); expect (String ("abcdefg", 4) == String (L"abcdefg", 4)); expect (String::charToString ('x') == "x"); expect (String::charToString (0) == String::empty); expect (s2 + "e" == "abcde" && s2 + 'e' == "abcde"); expect (s2 + L'e' == "abcde" && s2 + L"e" == "abcde"); expect (s1.equalsIgnoreCase ("abcD") && s1 < "abce" && s1 > "abbb"); expect (s1.startsWith ("ab") && s1.startsWith ("abcd") && ! s1.startsWith ("abcde")); expect (s1.startsWithIgnoreCase ("aB") && s1.endsWithIgnoreCase ("CD")); expect (s1.endsWith ("bcd") && ! s1.endsWith ("aabcd")); expectEquals (s1.indexOf (String::empty), 0); expectEquals (s1.indexOfIgnoreCase (String::empty), 0); expect (s1.startsWith (String::empty) && s1.endsWith (String::empty) && s1.contains (String::empty)); expect (s1.contains ("cd") && s1.contains ("ab") && s1.contains ("abcd")); expect (s1.containsChar ('a')); expect (! s1.containsChar ('x')); expect (! s1.containsChar (0)); expect (String ("abc foo bar").containsWholeWord ("abc") && String ("abc foo bar").containsWholeWord ("abc")); } { beginTest ("Operations"); String s ("012345678"); expect (s.hashCode() != 0); expect (s.hashCode64() != 0); expect (s.hashCode() != (s + s).hashCode()); expect (s.hashCode64() != (s + s).hashCode64()); expect (s.compare (String ("012345678")) == 0); expect (s.compare (String ("012345679")) < 0); expect (s.compare (String ("012345676")) > 0); expect (String("a").compareNatural ("A") == 0); expect (String("A").compareNatural ("B") < 0); expect (String("a").compareNatural ("B") < 0); expect (String("10").compareNatural ("2") > 0); expect (String("Abc 10").compareNatural ("aBC 2") > 0); expect (String("Abc 1").compareNatural ("aBC 2") < 0); expect (s.substring (2, 3) == String::charToString (s[2])); expect (s.substring (0, 1) == String::charToString (s[0])); expect (s.getLastCharacter() == s [s.length() - 1]); expect (String::charToString (s.getLastCharacter()) == s.getLastCharacters (1)); expect (s.substring (0, 3) == L"012"); expect (s.substring (0, 100) == s); expect (s.substring (-1, 100) == s); expect (s.substring (3) == "345678"); expect (s.indexOf (String (L"45")) == 4); expect (String ("444445").indexOf ("45") == 4); expect (String ("444445").lastIndexOfChar ('4') == 4); expect (String ("45454545x").lastIndexOf (String (L"45")) == 6); expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7); expect (String ("45454545x").lastIndexOfAnyOf (String (L"456x")) == 8); expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("aB") == 6); expect (s.indexOfChar (L'4') == 4); expect (s + s == "012345678012345678"); expect (s.startsWith (s)); expect (s.startsWith (s.substring (0, 4))); expect (s.startsWith (s.dropLastCharacters (4))); expect (s.endsWith (s.substring (5))); expect (s.endsWith (s)); expect (s.contains (s.substring (3, 6))); expect (s.contains (s.substring (3))); expect (s.startsWithChar (s[0])); expect (s.endsWithChar (s.getLastCharacter())); expect (s [s.length()] == 0); expect (String ("abcdEFGH").toLowerCase() == String ("abcdefgh")); expect (String ("abcdEFGH").toUpperCase() == String ("ABCDEFGH")); String s2 ("123"); s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; s2 += "xyz"; expect (s2 == "1234567890xyz"); s2 += (int) 123; expect (s2 == "1234567890xyz123"); s2 += (int64) 123; expect (s2 == "1234567890xyz123123"); beginTest ("Numeric conversions"); expect (String::empty.getIntValue() == 0); expect (String::empty.getDoubleValue() == 0.0); expect (String::empty.getFloatValue() == 0.0f); expect (s.getIntValue() == 12345678); expect (s.getLargeIntValue() == (int64) 12345678); expect (s.getDoubleValue() == 12345678.0); expect (s.getFloatValue() == 12345678.0f); expect (String (-1234).getIntValue() == -1234); expect (String ((int64) -1234).getLargeIntValue() == -1234); expect (String (-1234.56).getDoubleValue() == -1234.56); expect (String (-1234.56f).getFloatValue() == -1234.56f); expect (String (std::numeric_limits::max()).getIntValue() == std::numeric_limits::max()); expect (String (std::numeric_limits::min()).getIntValue() == std::numeric_limits::min()); expect (String (std::numeric_limits::max()).getLargeIntValue() == std::numeric_limits::max()); expect (String (std::numeric_limits::min()).getLargeIntValue() == std::numeric_limits::min()); expect (("xyz" + s).getTrailingIntValue() == s.getIntValue()); expect (s.getHexValue32() == 0x12345678); expect (s.getHexValue64() == (int64) 0x12345678); expect (String::toHexString (0x1234abcd).equalsIgnoreCase ("1234abcd")); expect (String::toHexString ((int64) 0x1234abcd).equalsIgnoreCase ("1234abcd")); expect (String::toHexString ((short) 0x12ab).equalsIgnoreCase ("12ab")); unsigned char data[] = { 1, 2, 3, 4, 0xa, 0xb, 0xc, 0xd }; expect (String::toHexString (data, 8, 0).equalsIgnoreCase ("010203040a0b0c0d")); expect (String::toHexString (data, 8, 1).equalsIgnoreCase ("01 02 03 04 0a 0b 0c 0d")); expect (String::toHexString (data, 8, 2).equalsIgnoreCase ("0102 0304 0a0b 0c0d")); beginTest ("Subsections"); String s3; s3 = "abcdeFGHIJ"; expect (s3.equalsIgnoreCase ("ABCdeFGhiJ")); expect (s3.compareIgnoreCase (L"ABCdeFGhiJ") == 0); expect (s3.containsIgnoreCase (s3.substring (3))); expect (s3.indexOfAnyOf ("xyzf", 2, true) == 5); expect (s3.indexOfAnyOf (String (L"xyzf"), 2, false) == -1); expect (s3.indexOfAnyOf ("xyzF", 2, false) == 5); expect (s3.containsAnyOf (String (L"zzzFs"))); expect (s3.startsWith ("abcd")); expect (s3.startsWithIgnoreCase (String (L"abCD"))); expect (s3.startsWith (String::empty)); expect (s3.startsWithChar ('a')); expect (s3.endsWith (String ("HIJ"))); expect (s3.endsWithIgnoreCase (String (L"Hij"))); expect (s3.endsWith (String::empty)); expect (s3.endsWithChar (L'J')); expect (s3.indexOf ("HIJ") == 7); expect (s3.indexOf (String (L"HIJK")) == -1); expect (s3.indexOfIgnoreCase ("hij") == 7); expect (s3.indexOfIgnoreCase (String (L"hijk")) == -1); expect (s3.toStdString() == s3.toRawUTF8()); String s4 (s3); s4.append (String ("xyz123"), 3); expect (s4 == s3 + "xyz"); expect (String (1234) < String (1235)); expect (String (1235) > String (1234)); expect (String (1234) >= String (1234)); expect (String (1234) <= String (1234)); expect (String (1235) >= String (1234)); expect (String (1234) <= String (1235)); String s5 ("word word2 word3"); expect (s5.containsWholeWord (String ("word2"))); expect (s5.indexOfWholeWord ("word2") == 5); expect (s5.containsWholeWord (String (L"word"))); expect (s5.containsWholeWord ("word3")); expect (s5.containsWholeWord (s5)); expect (s5.containsWholeWordIgnoreCase (String (L"Word2"))); expect (s5.indexOfWholeWordIgnoreCase ("Word2") == 5); expect (s5.containsWholeWordIgnoreCase (String (L"Word"))); expect (s5.containsWholeWordIgnoreCase ("Word3")); expect (! s5.containsWholeWordIgnoreCase (String (L"Wordx"))); expect (! s5.containsWholeWordIgnoreCase ("xWord2")); expect (s5.containsNonWhitespaceChars()); expect (s5.containsOnly ("ordw23 ")); expect (! String (" \n\r\t").containsNonWhitespaceChars()); expect (s5.matchesWildcard (String (L"wor*"), false)); expect (s5.matchesWildcard ("wOr*", true)); expect (s5.matchesWildcard (String (L"*word3"), true)); expect (s5.matchesWildcard ("*word?", true)); expect (s5.matchesWildcard (String (L"Word*3"), true)); expect (! s5.matchesWildcard (String (L"*34"), true)); expect (String ("xx**y").matchesWildcard ("*y", true)); expect (String ("xx**y").matchesWildcard ("x*y", true)); expect (String ("xx**y").matchesWildcard ("xx*y", true)); expect (String ("xx**y").matchesWildcard ("xx*", true)); expect (String ("xx?y").matchesWildcard ("x??y", true)); expect (String ("xx?y").matchesWildcard ("xx?y", true)); expect (! String ("xx?y").matchesWildcard ("xx?y?", true)); expect (String ("xx?y").matchesWildcard ("xx??", true)); expectEquals (s5.fromFirstOccurrenceOf (String::empty, true, false), s5); expectEquals (s5.fromFirstOccurrenceOf ("xword2", true, false), s5.substring (100)); expectEquals (s5.fromFirstOccurrenceOf (String (L"word2"), true, false), s5.substring (5)); expectEquals (s5.fromFirstOccurrenceOf ("Word2", true, true), s5.substring (5)); expectEquals (s5.fromFirstOccurrenceOf ("word2", false, false), s5.getLastCharacters (6)); expectEquals (s5.fromFirstOccurrenceOf ("Word2", false, true), s5.getLastCharacters (6)); expectEquals (s5.fromLastOccurrenceOf (String::empty, true, false), s5); expectEquals (s5.fromLastOccurrenceOf ("wordx", true, false), s5); expectEquals (s5.fromLastOccurrenceOf ("word", true, false), s5.getLastCharacters (5)); expectEquals (s5.fromLastOccurrenceOf ("worD", true, true), s5.getLastCharacters (5)); expectEquals (s5.fromLastOccurrenceOf ("word", false, false), s5.getLastCharacters (1)); expectEquals (s5.fromLastOccurrenceOf ("worD", false, true), s5.getLastCharacters (1)); expect (s5.upToFirstOccurrenceOf (String::empty, true, false).isEmpty()); expectEquals (s5.upToFirstOccurrenceOf ("word4", true, false), s5); expectEquals (s5.upToFirstOccurrenceOf ("word2", true, false), s5.substring (0, 10)); expectEquals (s5.upToFirstOccurrenceOf ("Word2", true, true), s5.substring (0, 10)); expectEquals (s5.upToFirstOccurrenceOf ("word2", false, false), s5.substring (0, 5)); expectEquals (s5.upToFirstOccurrenceOf ("Word2", false, true), s5.substring (0, 5)); expectEquals (s5.upToLastOccurrenceOf (String::empty, true, false), s5); expectEquals (s5.upToLastOccurrenceOf ("zword", true, false), s5); expectEquals (s5.upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); expectEquals (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); expectEquals (s5.upToLastOccurrenceOf ("Word", true, true), s5.dropLastCharacters (1)); expectEquals (s5.upToLastOccurrenceOf ("word", false, false), s5.dropLastCharacters (5)); expectEquals (s5.upToLastOccurrenceOf ("Word", false, true), s5.dropLastCharacters (5)); expectEquals (s5.replace ("word", "xyz", false), String ("xyz xyz2 xyz3")); expect (s5.replace ("Word", "xyz", true) == "xyz xyz2 xyz3"); expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz"); expect (s5.replace ("Word", "", true) == " 2 3"); expectEquals (s5.replace ("Word2", "xyz", true), String ("word xyz word3")); expect (s5.replaceCharacter (L'w', 'x') != s5); expectEquals (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w'), s5); expect (s5.replaceCharacters ("wo", "xy") != s5); expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", "wo"), s5); expectEquals (s5.retainCharacters ("1wordxya"), String ("wordwordword")); expect (s5.retainCharacters (String::empty).isEmpty()); expect (s5.removeCharacters ("1wordxya") == " 2 3"); expectEquals (s5.removeCharacters (String::empty), s5); expect (s5.initialSectionContainingOnly ("word") == L"word"); expect (String ("word").initialSectionContainingOnly ("word") == L"word"); expectEquals (s5.initialSectionNotContaining (String ("xyz ")), String ("word")); expectEquals (s5.initialSectionNotContaining (String (";[:'/")), s5); expect (! s5.isQuotedString()); expect (s5.quoted().isQuotedString()); expect (! s5.quoted().unquoted().isQuotedString()); expect (! String ("x'").isQuotedString()); expect (String ("'x").isQuotedString()); String s6 (" \t xyz \t\r\n"); expectEquals (s6.trim(), String ("xyz")); expect (s6.trim().trim() == "xyz"); expectEquals (s5.trim(), s5); expectEquals (s6.trimStart().trimEnd(), s6.trim()); expectEquals (s6.trimStart().trimEnd(), s6.trimEnd().trimStart()); expectEquals (s6.trimStart().trimStart().trimEnd().trimEnd(), s6.trimEnd().trimStart()); expect (s6.trimStart() != s6.trimEnd()); expectEquals (("\t\r\n " + s6 + "\t\n \r").trim(), s6.trim()); expect (String::repeatedString ("xyz", 3) == L"xyzxyzxyz"); } { beginTest ("UTF conversions"); TestUTFConversion ::test (*this, r); TestUTFConversion ::test (*this, r); TestUTFConversion ::test (*this, r); } { beginTest ("StringArray"); StringArray s; s.addTokens ("4,3,2,1,0", ";,", "x"); expectEquals (s.size(), 5); expectEquals (s.joinIntoString ("-"), String ("4-3-2-1-0")); s.remove (2); expectEquals (s.joinIntoString ("--"), String ("4--3--1--0")); expectEquals (s.joinIntoString (String::empty), String ("4310")); s.clear(); expectEquals (s.joinIntoString ("x"), String::empty); StringArray toks; toks.addTokens ("x,,", ";,", ""); expectEquals (toks.size(), 3); expectEquals (toks.joinIntoString ("-"), String ("x--")); toks.clear(); toks.addTokens (",x,", ";,", ""); expectEquals (toks.size(), 3); expectEquals (toks.joinIntoString ("-"), String ("-x-")); toks.clear(); toks.addTokens ("x,'y,z',", ";,", "'"); expectEquals (toks.size(), 3); expectEquals (toks.joinIntoString ("-"), String ("x-'y,z'-")); } { beginTest ("var"); var v1 = 0; var v2 = 0.16; var v3 = "0.16"; var v4 = (int64) 0; var v5 = 0.0; expect (! v2.equals (v1)); expect (! v1.equals (v2)); expect (v2.equals (v3)); expect (! v3.equals (v1)); expect (! v1.equals (v3)); expect (v1.equals (v4)); expect (v4.equals (v1)); expect (v5.equals (v4)); expect (v4.equals (v5)); expect (! v2.equals (v4)); expect (! v4.equals (v2)); } } }; static StringTests stringUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_String.h000066400000000000000000001764461320201440200271750ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_STRING_H_INCLUDED #define JUCE_STRING_H_INCLUDED //============================================================================== /** The JUCE String class! Using a reference-counted internal representation, these strings are fast and efficient, and there are methods to do just about any operation you'll ever dream of. @see StringArray, StringPairArray */ class JUCE_API String { public: //============================================================================== /** Creates an empty string. @see empty */ String() noexcept; /** Creates a copy of another string. */ String (const String& other) noexcept; #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS String (String&& other) noexcept; #endif /** Creates a string from a zero-terminated ascii text string. The string passed-in must not contain any characters with a value above 127, because these can't be converted to unicode without knowing the original encoding that was used to create the string. If you attempt to pass-in values above 127, you'll get an assertion. To create strings with extended characters from UTF-8, you should explicitly call String (CharPointer_UTF8 ("my utf8 string..")). It's *highly* recommended that you use UTF-8 with escape characters in your source code to represent extended characters, because there's no other way to represent unicode strings in a way that isn't dependent on the compiler, source code editor and platform. */ String (const char* text); /** Creates a string from a string of 8-bit ascii characters. The string passed-in must not contain any characters with a value above 127, because these can't be converted to unicode without knowing the original encoding that was used to create the string. If you attempt to pass-in values above 127, you'll get an assertion. To create strings with extended characters from UTF-8, you should explicitly call String (CharPointer_UTF8 ("my utf8 string..")). It's *highly* recommended that you use UTF-8 with escape characters in your source code to represent extended characters, because there's no other way to represent unicode strings in a way that isn't dependent on the compiler, source code editor and platform. This will use up to the first maxChars characters of the string (or less if the string is actually shorter). */ String (const char* text, size_t maxChars); /** Creates a string from a wchar_t character string. Depending on the platform, this may be treated as either UTF-32 or UTF-16. */ String (const wchar_t* text); /** Creates a string from a wchar_t character string. Depending on the platform, this may be treated as either UTF-32 or UTF-16. */ String (const wchar_t* text, size_t maxChars); //============================================================================== /** Creates a string from a UTF-8 character string */ String (const CharPointer_UTF8 text); /** Creates a string from a UTF-8 character string */ String (const CharPointer_UTF8 text, size_t maxChars); /** Creates a string from a UTF-8 character string */ String (const CharPointer_UTF8 start, const CharPointer_UTF8 end); //============================================================================== /** Creates a string from a UTF-16 character string */ String (const CharPointer_UTF16 text); /** Creates a string from a UTF-16 character string */ String (const CharPointer_UTF16 text, size_t maxChars); /** Creates a string from a UTF-16 character string */ String (const CharPointer_UTF16 start, const CharPointer_UTF16 end); //============================================================================== /** Creates a string from a UTF-32 character string */ String (const CharPointer_UTF32 text); /** Creates a string from a UTF-32 character string */ String (const CharPointer_UTF32 text, size_t maxChars); /** Creates a string from a UTF-32 character string */ String (const CharPointer_UTF32 start, const CharPointer_UTF32 end); //============================================================================== /** Creates a string from an ASCII character string */ String (const CharPointer_ASCII text); /** Creates a string from a UTF-8 encoded std::string. */ String (const std::string&); //============================================================================== /** Creates a string from a single character. */ static String charToString (juce_wchar character); /** Destructor. */ ~String() noexcept; //============================================================================== /** This is an empty string that can be used whenever one is needed. It's better to use this than String() because it explains what's going on and is more efficient. */ static const String empty; /** This is the character encoding type used internally to store the string. By setting the value of JUCE_STRING_UTF_TYPE to 8, 16, or 32, you can change the internal storage format of the String class. UTF-8 uses the least space (if your strings contain few extended characters), but call operator[] involves iterating the string to find the required index. UTF-32 provides instant random access to its characters, but uses 4 bytes per character to store them. UTF-16 uses more space than UTF-8 and is also slow to index, but is the native wchar_t format used in Windows. It doesn't matter too much which format you pick, because the toUTF8(), toUTF16() and toUTF32() methods let you access the string's content in any of the other formats. */ #if (JUCE_STRING_UTF_TYPE == 32) typedef CharPointer_UTF32 CharPointerType; #elif (JUCE_STRING_UTF_TYPE == 16) typedef CharPointer_UTF16 CharPointerType; #elif (DOXYGEN || JUCE_STRING_UTF_TYPE == 8) typedef CharPointer_UTF8 CharPointerType; #else #error "You must set the value of JUCE_STRING_UTF_TYPE to be either 8, 16, or 32!" #endif //============================================================================== /** Generates a probably-unique 32-bit hashcode from this string. */ int hashCode() const noexcept; /** Generates a probably-unique 64-bit hashcode from this string. */ int64 hashCode64() const noexcept; /** Generates a probably-unique hashcode from this string. */ size_t hash() const noexcept; /** Returns the number of characters in the string. */ int length() const noexcept; //============================================================================== // Assignment and concatenation operators.. /** Replaces this string's contents with another string. */ String& operator= (const String& other) noexcept; #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS String& operator= (String&& other) noexcept; #endif /** Appends another string at the end of this one. */ String& operator+= (const String& stringToAppend); /** Appends another string at the end of this one. */ String& operator+= (const char* textToAppend); /** Appends another string at the end of this one. */ String& operator+= (const wchar_t* textToAppend); /** Appends a decimal number at the end of this string. */ String& operator+= (int numberToAppend); /** Appends a decimal number at the end of this string. */ String& operator+= (int64 numberToAppend); /** Appends a character at the end of this string. */ String& operator+= (char characterToAppend); /** Appends a character at the end of this string. */ String& operator+= (wchar_t characterToAppend); #if ! JUCE_NATIVE_WCHAR_IS_UTF32 /** Appends a character at the end of this string. */ String& operator+= (juce_wchar characterToAppend); #endif /** Appends a string to the end of this one. @param textToAppend the string to add @param maxCharsToTake the maximum number of characters to take from the string passed in */ void append (const String& textToAppend, size_t maxCharsToTake); /** Appends a string to the end of this one. @param startOfTextToAppend the start of the string to add. This must not be a nullptr @param endOfTextToAppend the end of the string to add. This must not be a nullptr */ void appendCharPointer (const CharPointerType startOfTextToAppend, const CharPointerType endOfTextToAppend); /** Appends a string to the end of this one. @param startOfTextToAppend the start of the string to add. This must not be a nullptr @param endOfTextToAppend the end of the string to add. This must not be a nullptr */ template void appendCharPointer (const CharPointer startOfTextToAppend, const CharPointer endOfTextToAppend) { jassert (startOfTextToAppend.getAddress() != nullptr && endOfTextToAppend.getAddress() != nullptr); size_t extraBytesNeeded = 0, numChars = 1; for (CharPointer t (startOfTextToAppend); t != endOfTextToAppend && ! t.isEmpty(); ++numChars) extraBytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); if (extraBytesNeeded > 0) { const size_t byteOffsetOfNull = getByteOffsetOfEnd(); preallocateBytes (byteOffsetOfNull + extraBytesNeeded); CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)) .writeWithCharLimit (startOfTextToAppend, (int) numChars); } } /** Appends a string to the end of this one. */ void appendCharPointer (const CharPointerType textToAppend); /** Appends a string to the end of this one. @param textToAppend the string to add @param maxCharsToTake the maximum number of characters to take from the string passed in */ template void appendCharPointer (const CharPointer textToAppend, size_t maxCharsToTake) { if (textToAppend.getAddress() != nullptr) { size_t extraBytesNeeded = 0, numChars = 1; for (CharPointer t (textToAppend); numChars <= maxCharsToTake && ! t.isEmpty(); ++numChars) extraBytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); if (extraBytesNeeded > 0) { const size_t byteOffsetOfNull = getByteOffsetOfEnd(); preallocateBytes (byteOffsetOfNull + extraBytesNeeded); CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)) .writeWithCharLimit (textToAppend, (int) numChars); } } } /** Appends a string to the end of this one. */ template void appendCharPointer (const CharPointer textToAppend) { appendCharPointer (textToAppend, std::numeric_limits::max()); } //============================================================================== // Comparison methods.. /** Returns true if the string contains no characters. Note that there's also an isNotEmpty() method to help write readable code. @see containsNonWhitespaceChars() */ inline bool isEmpty() const noexcept { return text.isEmpty(); } /** Returns true if the string contains at least one character. Note that there's also an isEmpty() method to help write readable code. @see containsNonWhitespaceChars() */ inline bool isNotEmpty() const noexcept { return ! text.isEmpty(); } /** Resets this string to be empty. */ void clear() noexcept; /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const String& other) const noexcept; /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (StringRef other) const noexcept; /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const wchar_t* other) const noexcept; /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const char* other) const noexcept; /** Case-sensitive comparison with another string. @returns 0 if the two strings are identical; negative if this string comes before the other one alphabetically, or positive if it comes after it. */ int compare (const String& other) const noexcept; /** Case-sensitive comparison with another string. @returns 0 if the two strings are identical; negative if this string comes before the other one alphabetically, or positive if it comes after it. */ int compare (const char* other) const noexcept; /** Case-sensitive comparison with another string. @returns 0 if the two strings are identical; negative if this string comes before the other one alphabetically, or positive if it comes after it. */ int compare (const wchar_t* other) const noexcept; /** Case-insensitive comparison with another string. @returns 0 if the two strings are identical; negative if this string comes before the other one alphabetically, or positive if it comes after it. */ int compareIgnoreCase (const String& other) const noexcept; /** Compares two strings, taking into account textual characteristics like numbers and spaces. This comparison is case-insensitive and can detect words and embedded numbers in the strings, making it good for sorting human-readable lists of things like filenames. @returns 0 if the two strings are identical; negative if this string comes before the other one alphabetically, or positive if it comes after it. */ int compareNatural (StringRef other) const noexcept; /** Tests whether the string begins with another string. If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool startsWith (StringRef text) const noexcept; /** Tests whether the string begins with a particular character. If the character is 0, this will always return false. Uses a case-sensitive comparison. */ bool startsWithChar (juce_wchar character) const noexcept; /** Tests whether the string begins with another string. If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ bool startsWithIgnoreCase (StringRef text) const noexcept; /** Tests whether the string ends with another string. If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool endsWith (StringRef text) const noexcept; /** Tests whether the string ends with a particular character. If the character is 0, this will always return false. Uses a case-sensitive comparison. */ bool endsWithChar (juce_wchar character) const noexcept; /** Tests whether the string ends with another string. If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ bool endsWithIgnoreCase (StringRef text) const noexcept; /** Tests whether the string contains another substring. If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ bool contains (StringRef text) const noexcept; /** Tests whether the string contains a particular character. Uses a case-sensitive comparison. */ bool containsChar (juce_wchar character) const noexcept; /** Tests whether the string contains another substring. Uses a case-insensitive comparison. */ bool containsIgnoreCase (StringRef text) const noexcept; /** Tests whether the string contains another substring as a distinct word. @returns true if the string contains this word, surrounded by non-alphanumeric characters @see indexOfWholeWord, containsWholeWordIgnoreCase */ bool containsWholeWord (StringRef wordToLookFor) const noexcept; /** Tests whether the string contains another substring as a distinct word. @returns true if the string contains this word, surrounded by non-alphanumeric characters @see indexOfWholeWordIgnoreCase, containsWholeWord */ bool containsWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept; /** Finds an instance of another substring if it exists as a distinct word. @returns if the string contains this word, surrounded by non-alphanumeric characters, then this will return the index of the start of the substring. If it isn't found, then it will return -1 @see indexOfWholeWordIgnoreCase, containsWholeWord */ int indexOfWholeWord (StringRef wordToLookFor) const noexcept; /** Finds an instance of another substring if it exists as a distinct word. @returns if the string contains this word, surrounded by non-alphanumeric characters, then this will return the index of the start of the substring. If it isn't found, then it will return -1 @see indexOfWholeWord, containsWholeWordIgnoreCase */ int indexOfWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept; /** Looks for any of a set of characters in the string. Uses a case-sensitive comparison. @returns true if the string contains any of the characters from the string that is passed in. */ bool containsAnyOf (StringRef charactersItMightContain) const noexcept; /** Looks for a set of characters in the string. Uses a case-sensitive comparison. @returns Returns false if any of the characters in this string do not occur in the parameter string. If this string is empty, the return value will always be true. */ bool containsOnly (StringRef charactersItMightContain) const noexcept; /** Returns true if this string contains any non-whitespace characters. This will return false if the string contains only whitespace characters, or if it's empty. It is equivalent to calling "myString.trim().isNotEmpty()". */ bool containsNonWhitespaceChars() const noexcept; /** Returns true if the string matches this simple wildcard expression. So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. This isn't a full-blown regex though! The only wildcard characters supported are "*" and "?". It's mainly intended for filename pattern matching. */ bool matchesWildcard (StringRef wildcard, bool ignoreCase) const noexcept; //============================================================================== // Substring location methods.. /** Searches for a character inside this string. Uses a case-sensitive comparison. @returns the index of the first occurrence of the character in this string, or -1 if it's not found. */ int indexOfChar (juce_wchar characterToLookFor) const noexcept; /** Searches for a character inside this string. Uses a case-sensitive comparison. @param startIndex the index from which the search should proceed @param characterToLookFor the character to look for @returns the index of the first occurrence of the character in this string, or -1 if it's not found. */ int indexOfChar (int startIndex, juce_wchar characterToLookFor) const noexcept; /** Returns the index of the first character that matches one of the characters passed-in to this method. This scans the string, beginning from the startIndex supplied, and if it finds a character that appears in the string charactersToLookFor, it returns its index. If none of these characters are found, it returns -1. If ignoreCase is true, the comparison will be case-insensitive. @see indexOfChar, lastIndexOfAnyOf */ int indexOfAnyOf (StringRef charactersToLookFor, int startIndex = 0, bool ignoreCase = false) const noexcept; /** Searches for a substring within this string. Uses a case-sensitive comparison. @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return 0. */ int indexOf (StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-sensitive comparison. @param startIndex the index from which the search should proceed @param textToLookFor the string to search for @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ int indexOf (int startIndex, StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-insensitive comparison. @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return 0. */ int indexOfIgnoreCase (StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-insensitive comparison. @param startIndex the index from which the search should proceed @param textToLookFor the string to search for @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ int indexOfIgnoreCase (int startIndex, StringRef textToLookFor) const noexcept; /** Searches for a character inside this string (working backwards from the end of the string). Uses a case-sensitive comparison. @returns the index of the last occurrence of the character in this string, or -1 if it's not found. */ int lastIndexOfChar (juce_wchar character) const noexcept; /** Searches for a substring inside this string (working backwards from the end of the string). Uses a case-sensitive comparison. @returns the index of the start of the last occurrence of the substring within this string, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ int lastIndexOf (StringRef textToLookFor) const noexcept; /** Searches for a substring inside this string (working backwards from the end of the string). Uses a case-insensitive comparison. @returns the index of the start of the last occurrence of the substring within this string, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ int lastIndexOfIgnoreCase (StringRef textToLookFor) const noexcept; /** Returns the index of the last character in this string that matches one of the characters passed-in to this method. This scans the string backwards, starting from its end, and if it finds a character that appears in the string charactersToLookFor, it returns its index. If none of these characters are found, it returns -1. If ignoreCase is true, the comparison will be case-insensitive. @see lastIndexOf, indexOfAnyOf */ int lastIndexOfAnyOf (StringRef charactersToLookFor, bool ignoreCase = false) const noexcept; //============================================================================== // Substring extraction and manipulation methods.. /** Returns the character at this index in the string. In a release build, no checks are made to see if the index is within a valid range, so be careful! In a debug build, the index is checked and an assertion fires if it's out-of-range. Also beware that depending on the encoding format that the string is using internally, this method may execute in either O(1) or O(n) time, so be careful when using it in your algorithms. If you're scanning through a string to inspect its characters, you should never use this operator for random access, it's far more efficient to call getCharPointer() to return a pointer, and then to use that to iterate the string. @see getCharPointer */ juce_wchar operator[] (int index) const noexcept; /** Returns the final character of the string. If the string is empty this will return 0. */ juce_wchar getLastCharacter() const noexcept; //============================================================================== /** Returns a subsection of the string. If the range specified is beyond the limits of the string, as much as possible is returned. @param startIndex the index of the start of the substring needed @param endIndex all characters from startIndex up to (but not including) this index are returned @see fromFirstOccurrenceOf, dropLastCharacters, getLastCharacters, upToFirstOccurrenceOf */ String substring (int startIndex, int endIndex) const; /** Returns a section of the string, starting from a given position. @param startIndex the first character to include. If this is beyond the end of the string, an empty string is returned. If it is zero or less, the whole string is returned. @returns the substring from startIndex up to the end of the string @see dropLastCharacters, getLastCharacters, fromFirstOccurrenceOf, upToFirstOccurrenceOf, fromLastOccurrenceOf */ String substring (int startIndex) const; /** Returns a version of this string with a number of characters removed from the end. @param numberToDrop the number of characters to drop from the end of the string. If this is greater than the length of the string, an empty string will be returned. If zero or less, the original string will be returned. @see substring, fromFirstOccurrenceOf, upToFirstOccurrenceOf, fromLastOccurrenceOf, getLastCharacter */ String dropLastCharacters (int numberToDrop) const; /** Returns a number of characters from the end of the string. This returns the last numCharacters characters from the end of the string. If the string is shorter than numCharacters, the whole string is returned. @see substring, dropLastCharacters, getLastCharacter */ String getLastCharacters (int numCharacters) const; //============================================================================== /** Returns a section of the string starting from a given substring. This will search for the first occurrence of the given substring, and return the section of the string starting from the point where this is found (optionally not including the substring itself). e.g. for the string "123456", fromFirstOccurrenceOf ("34", true) would return "3456", and fromFirstOccurrenceOf ("34", false) would return "56". If the substring isn't found, the method will return an empty string. If ignoreCase is true, the comparison will be case-insensitive. @see upToFirstOccurrenceOf, fromLastOccurrenceOf */ String fromFirstOccurrenceOf (StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const; /** Returns a section of the string starting from the last occurrence of a given substring. Similar to fromFirstOccurrenceOf(), but using the last occurrence of the substring, and unlike fromFirstOccurrenceOf(), if the substring isn't found, this method will return the whole of the original string. @see fromFirstOccurrenceOf, upToLastOccurrenceOf */ String fromLastOccurrenceOf (StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const; /** Returns the start of this string, up to the first occurrence of a substring. This will search for the first occurrence of a given substring, and then return a copy of the string, up to the position of this substring, optionally including or excluding the substring itself in the result. e.g. for the string "123456", upTo ("34", false) would return "12", and upTo ("34", true) would return "1234". If the substring isn't found, this will return the whole of the original string. @see upToLastOccurrenceOf, fromFirstOccurrenceOf */ String upToFirstOccurrenceOf (StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const; /** Returns the start of this string, up to the last occurrence of a substring. Similar to upToFirstOccurrenceOf(), but this finds the last occurrence rather than the first. If the substring isn't found, this will return the whole of the original string. @see upToFirstOccurrenceOf, fromFirstOccurrenceOf */ String upToLastOccurrenceOf (StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const; //============================================================================== /** Returns a copy of this string with any whitespace characters removed from the start and end. */ String trim() const; /** Returns a copy of this string with any whitespace characters removed from the start. */ String trimStart() const; /** Returns a copy of this string with any whitespace characters removed from the end. */ String trimEnd() const; /** Returns a copy of this string, having removed a specified set of characters from its start. Characters are removed from the start of the string until it finds one that is not in the specified set, and then it stops. @param charactersToTrim the set of characters to remove. @see trim, trimStart, trimCharactersAtEnd */ String trimCharactersAtStart (StringRef charactersToTrim) const; /** Returns a copy of this string, having removed a specified set of characters from its end. Characters are removed from the end of the string until it finds one that is not in the specified set, and then it stops. @param charactersToTrim the set of characters to remove. @see trim, trimEnd, trimCharactersAtStart */ String trimCharactersAtEnd (StringRef charactersToTrim) const; //============================================================================== /** Returns an upper-case version of this string. */ String toUpperCase() const; /** Returns an lower-case version of this string. */ String toLowerCase() const; //============================================================================== /** Replaces a sub-section of the string with another string. This will return a copy of this string, with a set of characters from startIndex to startIndex + numCharsToReplace removed, and with a new string inserted in their place. Note that this is a const method, and won't alter the string itself. @param startIndex the first character to remove. If this is beyond the bounds of the string, it will be constrained to a valid range. @param numCharactersToReplace the number of characters to remove. If zero or less, no characters will be taken out. @param stringToInsert the new string to insert at startIndex after the characters have been removed. */ String replaceSection (int startIndex, int numCharactersToReplace, StringRef stringToInsert) const; /** Replaces all occurrences of a substring with another string. Returns a copy of this string, with any occurrences of stringToReplace swapped for stringToInsertInstead. Note that this is a const method, and won't alter the string itself. */ String replace (StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase = false) const; /** Returns a string with all occurrences of a character replaced with a different one. */ String replaceCharacter (juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const; /** Replaces a set of characters with another set. Returns a string in which each character from charactersToReplace has been replaced by the character at the equivalent position in newCharacters (so the two strings passed in must be the same length). e.g. replaceCharacters ("abc", "def") replaces 'a' with 'd', 'b' with 'e', etc. Note that this is a const method, and won't affect the string itself. */ String replaceCharacters (StringRef charactersToReplace, StringRef charactersToInsertInstead) const; /** Returns a version of this string that only retains a fixed set of characters. This will return a copy of this string, omitting any characters which are not found in the string passed-in. e.g. for "1122334455", retainCharacters ("432") would return "223344" Note that this is a const method, and won't alter the string itself. */ String retainCharacters (StringRef charactersToRetain) const; /** Returns a version of this string with a set of characters removed. This will return a copy of this string, omitting any characters which are found in the string passed-in. e.g. for "1122334455", removeCharacters ("432") would return "1155" Note that this is a const method, and won't alter the string itself. */ String removeCharacters (StringRef charactersToRemove) const; /** Returns a section from the start of the string that only contains a certain set of characters. This returns the leftmost section of the string, up to (and not including) the first character that doesn't appear in the string passed in. */ String initialSectionContainingOnly (StringRef permittedCharacters) const; /** Returns a section from the start of the string that only contains a certain set of characters. This returns the leftmost section of the string, up to (and not including) the first character that occurs in the string passed in. (If none of the specified characters are found in the string, the return value will just be the original string). */ String initialSectionNotContaining (StringRef charactersToStopAt) const; //============================================================================== /** Checks whether the string might be in quotation marks. @returns true if the string begins with a quote character (either a double or single quote). It is also true if there is whitespace before the quote, but it doesn't check the end of the string. @see unquoted, quoted */ bool isQuotedString() const; /** Removes quotation marks from around the string, (if there are any). Returns a copy of this string with any quotes removed from its ends. Quotes that aren't at the ends of the string are not affected. If there aren't any quotes, the original string is returned. Note that this is a const method, and won't alter the string itself. @see isQuotedString, quoted */ String unquoted() const; /** Adds quotation marks around a string. This will return a copy of the string with a quote at the start and end, (but won't add the quote if there's already one there, so it's safe to call this on strings that may already have quotes around them). Note that this is a const method, and won't alter the string itself. @param quoteCharacter the character to add at the start and end @see isQuotedString, unquoted */ String quoted (juce_wchar quoteCharacter = '"') const; //============================================================================== /** Creates a string which is a version of a string repeated and joined together. @param stringToRepeat the string to repeat @param numberOfTimesToRepeat how many times to repeat it */ static String repeatedString (StringRef stringToRepeat, int numberOfTimesToRepeat); /** Returns a copy of this string with the specified character repeatedly added to its beginning until the total length is at least the minimum length specified. */ String paddedLeft (juce_wchar padCharacter, int minimumLength) const; /** Returns a copy of this string with the specified character repeatedly added to its end until the total length is at least the minimum length specified. */ String paddedRight (juce_wchar padCharacter, int minimumLength) const; /** Creates a string from data in an unknown format. This looks at some binary data and tries to guess whether it's Unicode or 8-bit characters, then returns a string that represents it correctly. Should be able to handle Unicode endianness correctly, by looking at the first two bytes. */ static String createStringFromData (const void* data, int size); /** Creates a String from a printf-style parameter list. I don't like this method. I don't use it myself, and I recommend avoiding it and using the operator<< methods or pretty much anything else instead. It's only provided here because of the popular unrest that was stirred-up when I tried to remove it... If you're really determined to use it, at least make sure that you never, ever, pass any String objects to it as parameters. And bear in mind that internally, depending on the platform, it may be using wchar_t or char character types, so that even string literals can't be safely used as parameters if you're writing portable code. */ static String formatted (const String formatString, ... ); //============================================================================== // Numeric conversions.. /** Creates a string containing this signed 32-bit integer as a decimal number. @see getIntValue, getFloatValue, getDoubleValue, toHexString */ explicit String (int decimalInteger); /** Creates a string containing this unsigned 32-bit integer as a decimal number. @see getIntValue, getFloatValue, getDoubleValue, toHexString */ explicit String (unsigned int decimalInteger); /** Creates a string containing this signed 16-bit integer as a decimal number. @see getIntValue, getFloatValue, getDoubleValue, toHexString */ explicit String (short decimalInteger); /** Creates a string containing this unsigned 16-bit integer as a decimal number. @see getIntValue, getFloatValue, getDoubleValue, toHexString */ explicit String (unsigned short decimalInteger); /** Creates a string containing this signed 64-bit integer as a decimal number. @see getLargeIntValue, getFloatValue, getDoubleValue, toHexString */ explicit String (int64 largeIntegerValue); /** Creates a string containing this unsigned 64-bit integer as a decimal number. @see getLargeIntValue, getFloatValue, getDoubleValue, toHexString */ explicit String (uint64 largeIntegerValue); /** Creates a string representing this floating-point number. @param floatValue the value to convert to a string @see getDoubleValue, getIntValue */ explicit String (float floatValue); /** Creates a string representing this floating-point number. @param doubleValue the value to convert to a string @see getFloatValue, getIntValue */ explicit String (double doubleValue); /** Creates a string representing this floating-point number. @param floatValue the value to convert to a string @param numberOfDecimalPlaces if this is > 0, it will format the number using that many decimal places, and will not use exponent notation. If 0 or less, it will use exponent notation if necessary. @see getDoubleValue, getIntValue */ String (float floatValue, int numberOfDecimalPlaces); /** Creates a string representing this floating-point number. @param doubleValue the value to convert to a string @param numberOfDecimalPlaces if this is > 0, it will format the number using that many decimal places, and will not use exponent notation. If 0 or less, it will use exponent notation if necessary. @see getFloatValue, getIntValue */ String (double doubleValue, int numberOfDecimalPlaces); /** Reads the value of the string as a decimal number (up to 32 bits in size). @returns the value of the string as a 32 bit signed base-10 integer. @see getTrailingIntValue, getHexValue32, getHexValue64 */ int getIntValue() const noexcept; /** Reads the value of the string as a decimal number (up to 64 bits in size). @returns the value of the string as a 64 bit signed base-10 integer. */ int64 getLargeIntValue() const noexcept; /** Parses a decimal number from the end of the string. This will look for a value at the end of the string. e.g. for "321 xyz654" it will return 654; for "2 3 4" it'll return 4. Negative numbers are not handled, so "xyz-5" returns 5. @see getIntValue */ int getTrailingIntValue() const noexcept; /** Parses this string as a floating point number. @returns the value of the string as a 32-bit floating point value. @see getDoubleValue */ float getFloatValue() const noexcept; /** Parses this string as a floating point number. @returns the value of the string as a 64-bit floating point value. @see getFloatValue */ double getDoubleValue() const noexcept; /** Parses the string as a hexadecimal number. Non-hexadecimal characters in the string are ignored. If the string contains too many characters, then the lowest significant digits are returned, e.g. "ffff12345678" would produce 0x12345678. @returns a 32-bit number which is the value of the string in hex. */ int getHexValue32() const noexcept; /** Parses the string as a hexadecimal number. Non-hexadecimal characters in the string are ignored. If the string contains too many characters, then the lowest significant digits are returned, e.g. "ffff1234567812345678" would produce 0x1234567812345678. @returns a 64-bit number which is the value of the string in hex. */ int64 getHexValue64() const noexcept; /** Creates a string representing this 32-bit value in hexadecimal. */ static String toHexString (int number); /** Creates a string representing this 64-bit value in hexadecimal. */ static String toHexString (int64 number); /** Creates a string representing this 16-bit value in hexadecimal. */ static String toHexString (short number); /** Creates a string containing a hex dump of a block of binary data. @param data the binary data to use as input @param size how many bytes of data to use @param groupSize how many bytes are grouped together before inserting a space into the output. e.g. group size 0 has no spaces, group size 1 looks like: "be a1 c2 ff", group size 2 looks like "bea1 c2ff". */ static String toHexString (const void* data, int size, int groupSize = 1); //============================================================================== /** Returns the character pointer currently being used to store this string. Because it returns a reference to the string's internal data, the pointer that is returned must not be stored anywhere, as it can be deleted whenever the string changes. */ inline CharPointerType getCharPointer() const noexcept { return text; } /** Returns a pointer to a UTF-8 version of this string. Because it returns a reference to the string's internal data, the pointer that is returned must not be stored anywhere, as it can be deleted whenever the string changes. To find out how many bytes you need to store this string as UTF-8, you can call CharPointer_UTF8::getBytesRequiredFor (myString.getCharPointer()) @see toRawUTF8, getCharPointer, toUTF16, toUTF32 */ CharPointer_UTF8 toUTF8() const; /** Returns a pointer to a UTF-8 version of this string. Because it returns a reference to the string's internal data, the pointer that is returned must not be stored anywhere, as it can be deleted whenever the string changes. To find out how many bytes you need to store this string as UTF-8, you can call CharPointer_UTF8::getBytesRequiredFor (myString.getCharPointer()) @see getCharPointer, toUTF8, toUTF16, toUTF32 */ const char* toRawUTF8() const; /** Returns a pointer to a UTF-16 version of this string. Because it returns a reference to the string's internal data, the pointer that is returned must not be stored anywhere, as it can be deleted whenever the string changes. To find out how many bytes you need to store this string as UTF-16, you can call CharPointer_UTF16::getBytesRequiredFor (myString.getCharPointer()) @see getCharPointer, toUTF8, toUTF32 */ CharPointer_UTF16 toUTF16() const; /** Returns a pointer to a UTF-32 version of this string. Because it returns a reference to the string's internal data, the pointer that is returned must not be stored anywhere, as it can be deleted whenever the string changes. @see getCharPointer, toUTF8, toUTF16 */ CharPointer_UTF32 toUTF32() const; /** Returns a pointer to a wchar_t version of this string. Because it returns a reference to the string's internal data, the pointer that is returned must not be stored anywhere, as it can be deleted whenever the string changes. Bear in mind that the wchar_t type is different on different platforms, so on Windows, this will be equivalent to calling toUTF16(), on unix it'll be the same as calling toUTF32(), etc. @see getCharPointer, toUTF8, toUTF16, toUTF32 */ const wchar_t* toWideCharPointer() const; /** */ std::string toStdString() const; //============================================================================== /** Creates a String from a UTF-8 encoded buffer. If the size is < 0, it'll keep reading until it hits a zero. */ static String fromUTF8 (const char* utf8buffer, int bufferSizeBytes = -1); /** Returns the number of bytes required to represent this string as UTF8. The number returned does NOT include the trailing zero. @see toUTF8, copyToUTF8 */ size_t getNumBytesAsUTF8() const noexcept; //============================================================================== /** Copies the string to a buffer as UTF-8 characters. Returns the number of bytes copied to the buffer, including the terminating null character. To find out how many bytes you need to store this string as UTF-8, you can call CharPointer_UTF8::getBytesRequiredFor (myString.getCharPointer()) @param destBuffer the place to copy it to; if this is a null pointer, the method just returns the number of bytes required (including the terminating null character). @param maxBufferSizeBytes the size of the destination buffer, in bytes. If the string won't fit, it'll put in as many as it can while still allowing for a terminating null char at the end, and will return the number of bytes that were actually used. @see CharPointer_UTF8::writeWithDestByteLimit */ size_t copyToUTF8 (CharPointer_UTF8::CharType* destBuffer, size_t maxBufferSizeBytes) const noexcept; /** Copies the string to a buffer as UTF-16 characters. Returns the number of bytes copied to the buffer, including the terminating null character. To find out how many bytes you need to store this string as UTF-16, you can call CharPointer_UTF16::getBytesRequiredFor (myString.getCharPointer()) @param destBuffer the place to copy it to; if this is a null pointer, the method just returns the number of bytes required (including the terminating null character). @param maxBufferSizeBytes the size of the destination buffer, in bytes. If the string won't fit, it'll put in as many as it can while still allowing for a terminating null char at the end, and will return the number of bytes that were actually used. @see CharPointer_UTF16::writeWithDestByteLimit */ size_t copyToUTF16 (CharPointer_UTF16::CharType* destBuffer, size_t maxBufferSizeBytes) const noexcept; /** Copies the string to a buffer as UTF-32 characters. Returns the number of bytes copied to the buffer, including the terminating null character. To find out how many bytes you need to store this string as UTF-32, you can call CharPointer_UTF32::getBytesRequiredFor (myString.getCharPointer()) @param destBuffer the place to copy it to; if this is a null pointer, the method just returns the number of bytes required (including the terminating null character). @param maxBufferSizeBytes the size of the destination buffer, in bytes. If the string won't fit, it'll put in as many as it can while still allowing for a terminating null char at the end, and will return the number of bytes that were actually used. @see CharPointer_UTF32::writeWithDestByteLimit */ size_t copyToUTF32 (CharPointer_UTF32::CharType* destBuffer, size_t maxBufferSizeBytes) const noexcept; //============================================================================== /** Increases the string's internally allocated storage. Although the string's contents won't be affected by this call, it will increase the amount of memory allocated internally for the string to grow into. If you're about to make a large number of calls to methods such as += or <<, it's more efficient to preallocate enough extra space beforehand, so that these methods won't have to keep resizing the string to append the extra characters. @param numBytesNeeded the number of bytes to allocate storage for. If this value is less than the currently allocated size, it will have no effect. */ void preallocateBytes (size_t numBytesNeeded); /** Swaps the contents of this string with another one. This is a very fast operation, as no allocation or copying needs to be done. */ void swapWith (String& other) noexcept; //============================================================================== #if JUCE_MAC || JUCE_IOS || DOXYGEN /** OSX ONLY - Creates a String from an OSX CFString. */ static String fromCFString (CFStringRef cfString); /** OSX ONLY - Converts this string to a CFString. Remember that you must use CFRelease() to free the returned string when you're finished with it. */ CFStringRef toCFString() const; /** OSX ONLY - Returns a copy of this string in which any decomposed unicode characters have been converted to their precomposed equivalents. */ String convertToPrecomposedUnicode() const; #endif /** Returns the number of String objects which are currently sharing the same internal data as this one. */ int getReferenceCount() const noexcept; private: //============================================================================== CharPointerType text; //============================================================================== struct PreallocationBytes { explicit PreallocationBytes (size_t) noexcept; size_t numBytes; }; explicit String (const PreallocationBytes&); // This constructor preallocates a certain amount of memory size_t getByteOffsetOfEnd() const noexcept; JUCE_DEPRECATED (String (const String&, size_t)); // This private cast operator should prevent strings being accidentally cast // to bools (this is possible because the compiler can add an implicit cast // via a const char*) operator bool() const noexcept { return false; } }; //============================================================================== /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (const char* string1, const String& string2); /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t* string1, const String& string2); /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (char string1, const String& string2); /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (wchar_t string1, const String& string2); #if ! JUCE_NATIVE_WCHAR_IS_UTF32 /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (juce_wchar string1, const String& string2); #endif /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (String string1, const String& string2); /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (String string1, const char* string2); /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (String string1, const wchar_t* string2); /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (String string1, char characterToAppend); /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (String string1, wchar_t characterToAppend); #if ! JUCE_NATIVE_WCHAR_IS_UTF32 /** Concatenates two strings. */ JUCE_API String JUCE_CALLTYPE operator+ (String string1, juce_wchar characterToAppend); #endif //============================================================================== /** Appends a character at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, char characterToAppend); /** Appends a character at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, wchar_t characterToAppend); #if ! JUCE_NATIVE_WCHAR_IS_UTF32 /** Appends a character at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, juce_wchar characterToAppend); #endif /** Appends a string to the end of the first one. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const char* string2); /** Appends a string to the end of the first one. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const wchar_t* string2); /** Appends a string to the end of the first one. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const String& string2); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, short number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, long number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int64 number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, uint64 number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, float number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, double number); //============================================================================== /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const char* string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const wchar_t* string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF8 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF16 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF32 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const char* string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const wchar_t* string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF8 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF16 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF32 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator> (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator< (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator>= (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator<= (const String& string1, const String& string2) noexcept; //============================================================================== /** This operator allows you to write a juce String directly to std output streams. This is handy for writing strings to std::cout, std::cerr, etc. */ template std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostream & stream, const String& stringToWrite) { return stream << stringToWrite.toRawUTF8(); } /** This operator allows you to write a juce String directly to std output streams. This is handy for writing strings to std::wcout, std::wcerr, etc. */ template std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostream & stream, const String& stringToWrite) { return stream << stringToWrite.toWideCharPointer(); } /** Writes a string to an OutputStream as UTF8. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& stringToWrite); /** Writes a string to an OutputStream as UTF8. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef stringToWrite); #endif // JUCE_STRING_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp000066400000000000000000000326251320201440200305150ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ StringArray::StringArray() noexcept { } StringArray::StringArray (const StringArray& other) : strings (other.strings) { } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS StringArray::StringArray (StringArray&& other) noexcept : strings (static_cast &&> (other.strings)) { } #endif StringArray::StringArray (const String& firstValue) { strings.add (firstValue); } StringArray::StringArray (const String* initialStrings, int numberOfStrings) { strings.addArray (initialStrings, numberOfStrings); } StringArray::StringArray (const char* const* initialStrings) { strings.addNullTerminatedArray (initialStrings); } StringArray::StringArray (const char* const* initialStrings, int numberOfStrings) { strings.addArray (initialStrings, numberOfStrings); } StringArray::StringArray (const wchar_t* const* initialStrings) { strings.addNullTerminatedArray (initialStrings); } StringArray::StringArray (const wchar_t* const* initialStrings, int numberOfStrings) { strings.addArray (initialStrings, numberOfStrings); } StringArray& StringArray::operator= (const StringArray& other) { strings = other.strings; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS StringArray& StringArray::operator= (StringArray&& other) noexcept { strings = static_cast &&> (other.strings); return *this; } #endif #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS StringArray::StringArray (const std::initializer_list& stringList) { strings.addArray (stringList); } #endif StringArray::~StringArray() { } bool StringArray::operator== (const StringArray& other) const noexcept { return strings == other.strings; } bool StringArray::operator!= (const StringArray& other) const noexcept { return ! operator== (other); } void StringArray::swapWith (StringArray& other) noexcept { strings.swapWith (other.strings); } void StringArray::clear() { strings.clear(); } void StringArray::clearQuick() { strings.clearQuick(); } const String& StringArray::operator[] (const int index) const noexcept { if (isPositiveAndBelow (index, strings.size())) return strings.getReference (index); return String::empty; } String& StringArray::getReference (const int index) noexcept { return strings.getReference (index); } void StringArray::add (const String& newString) { strings.add (newString); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS void StringArray::add (String&& stringToAdd) { strings.add (static_cast (stringToAdd)); } #endif void StringArray::insert (const int index, const String& newString) { strings.insert (index, newString); } void StringArray::addIfNotAlreadyThere (const String& newString, const bool ignoreCase) { if (! contains (newString, ignoreCase)) add (newString); } void StringArray::addArray (const StringArray& otherArray, int startIndex, int numElementsToAdd) { if (startIndex < 0) { jassertfalse; startIndex = 0; } if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size()) numElementsToAdd = otherArray.size() - startIndex; while (--numElementsToAdd >= 0) strings.add (otherArray.strings.getReference (startIndex++)); } void StringArray::set (const int index, const String& newString) { strings.set (index, newString); } bool StringArray::contains (StringRef stringToLookFor, const bool ignoreCase) const { return indexOf (stringToLookFor, ignoreCase) >= 0; } int StringArray::indexOf (StringRef stringToLookFor, const bool ignoreCase, int i) const { if (i < 0) i = 0; const int numElements = size(); if (ignoreCase) { for (; i < numElements; ++i) if (strings.getReference(i).equalsIgnoreCase (stringToLookFor)) return i; } else { for (; i < numElements; ++i) if (stringToLookFor == strings.getReference (i)) return i; } return -1; } void StringArray::move (const int currentIndex, const int newIndex) noexcept { strings.move (currentIndex, newIndex); } //============================================================================== void StringArray::remove (const int index) { strings.remove (index); } void StringArray::removeString (StringRef stringToRemove, const bool ignoreCase) { if (ignoreCase) { for (int i = size(); --i >= 0;) if (strings.getReference(i).equalsIgnoreCase (stringToRemove)) strings.remove (i); } else { for (int i = size(); --i >= 0;) if (stringToRemove == strings.getReference (i)) strings.remove (i); } } void StringArray::removeRange (int startIndex, int numberToRemove) { strings.removeRange (startIndex, numberToRemove); } //============================================================================== void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) { if (removeWhitespaceStrings) { for (int i = size(); --i >= 0;) if (! strings.getReference(i).containsNonWhitespaceChars()) strings.remove (i); } else { for (int i = size(); --i >= 0;) if (strings.getReference(i).isEmpty()) strings.remove (i); } } void StringArray::trim() { for (int i = size(); --i >= 0;) { String& s = strings.getReference(i); s = s.trim(); } } //============================================================================== struct InternalStringArrayComparator_CaseSensitive { static int compareElements (String& s1, String& s2) noexcept { return s1.compare (s2); } }; struct InternalStringArrayComparator_CaseInsensitive { static int compareElements (String& s1, String& s2) noexcept { return s1.compareIgnoreCase (s2); } }; struct InternalStringArrayComparator_Natural { static int compareElements (String& s1, String& s2) noexcept { return s1.compareNatural (s2); } }; void StringArray::sort (const bool ignoreCase) { if (ignoreCase) { InternalStringArrayComparator_CaseInsensitive comp; strings.sort (comp); } else { InternalStringArrayComparator_CaseSensitive comp; strings.sort (comp); } } void StringArray::sortNatural() { InternalStringArrayComparator_Natural comp; strings.sort (comp); } //============================================================================== String StringArray::joinIntoString (StringRef separator, int start, int numberToJoin) const { const int last = (numberToJoin < 0) ? size() : jmin (size(), start + numberToJoin); if (start < 0) start = 0; if (start >= last) return String(); if (start == last - 1) return strings.getReference (start); const size_t separatorBytes = separator.text.sizeInBytes() - sizeof (String::CharPointerType::CharType); size_t bytesNeeded = separatorBytes * (size_t) (last - start - 1); for (int i = start; i < last; ++i) bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); String result; result.preallocateBytes (bytesNeeded); String::CharPointerType dest (result.getCharPointer()); while (start < last) { const String& s = strings.getReference (start); if (! s.isEmpty()) dest.writeAll (s.getCharPointer()); if (++start < last && separatorBytes > 0) dest.writeAll (separator.text); } dest.writeNull(); return result; } int StringArray::addTokens (StringRef text, const bool preserveQuotedStrings) { return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : ""); } int StringArray::addTokens (StringRef text, StringRef breakCharacters, StringRef quoteCharacters) { int num = 0; if (text.isNotEmpty()) { for (String::CharPointerType t (text.text);;) { String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t, breakCharacters.text, quoteCharacters.text)); strings.add (String (t, tokenEnd)); ++num; if (tokenEnd.isEmpty()) break; t = ++tokenEnd; } } return num; } int StringArray::addLines (StringRef sourceText) { int numLines = 0; String::CharPointerType text (sourceText.text); bool finished = text.isEmpty(); while (! finished) { for (String::CharPointerType startOfLine (text);;) { const String::CharPointerType endOfLine (text); switch (text.getAndAdvance()) { case 0: finished = true; break; case '\n': break; case '\r': if (*text == '\n') ++text; break; default: continue; } strings.add (String (startOfLine, endOfLine)); ++numLines; break; } } return numLines; } StringArray StringArray::fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings) { StringArray s; s.addTokens (stringToTokenise, preserveQuotedStrings); return s; } StringArray StringArray::fromTokens (StringRef stringToTokenise, StringRef breakCharacters, StringRef quoteCharacters) { StringArray s; s.addTokens (stringToTokenise, breakCharacters, quoteCharacters); return s; } StringArray StringArray::fromLines (StringRef stringToBreakUp) { StringArray s; s.addLines (stringToBreakUp); return s; } //============================================================================== void StringArray::removeDuplicates (const bool ignoreCase) { for (int i = 0; i < size() - 1; ++i) { const String s (strings.getReference(i)); for (int nextIndex = i + 1;;) { nextIndex = indexOf (s, ignoreCase, nextIndex); if (nextIndex < 0) break; strings.remove (nextIndex); } } } void StringArray::appendNumbersToDuplicates (const bool ignoreCase, const bool appendNumberToFirstInstance, CharPointer_UTF8 preNumberString, CharPointer_UTF8 postNumberString) { CharPointer_UTF8 defaultPre (" ("), defaultPost (")"); if (preNumberString.getAddress() == nullptr) preNumberString = defaultPre; if (postNumberString.getAddress() == nullptr) postNumberString = defaultPost; for (int i = 0; i < size() - 1; ++i) { String& s = strings.getReference(i); int nextIndex = indexOf (s, ignoreCase, i + 1); if (nextIndex >= 0) { const String original (s); int number = 0; if (appendNumberToFirstInstance) s = original + String (preNumberString) + String (++number) + String (postNumberString); else ++number; while (nextIndex >= 0) { set (nextIndex, (*this)[nextIndex] + String (preNumberString) + String (++number) + String (postNumberString)); nextIndex = indexOf (original, ignoreCase, nextIndex + 1); } } } } void StringArray::ensureStorageAllocated (int minNumElements) { strings.ensureStorageAllocated (minNumElements); } void StringArray::minimiseStorageOverheads() { strings.minimiseStorageOverheads(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h000066400000000000000000000445641320201440200301670ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_STRINGARRAY_H_INCLUDED #define JUCE_STRINGARRAY_H_INCLUDED //============================================================================== /** A special array for holding a list of strings. @see String, StringPairArray */ class JUCE_API StringArray { public: //============================================================================== /** Creates an empty string array */ StringArray() noexcept; /** Creates a copy of another string array */ StringArray (const StringArray&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS StringArray (StringArray&&) noexcept; #endif /** Creates an array containing a single string. */ explicit StringArray (const String& firstValue); /** Creates an array from a raw array of strings. @param strings an array of strings to add @param numberOfStrings how many items there are in the array */ StringArray (const String* strings, int numberOfStrings); /** Creates a copy of an array of string literals. @param strings an array of strings to add. Null pointers in the array will be treated as empty strings @param numberOfStrings how many items there are in the array */ StringArray (const char* const* strings, int numberOfStrings); /** Creates a copy of a null-terminated array of string literals. Each item from the array passed-in is added, until it encounters a null pointer, at which point it stops. */ explicit StringArray (const char* const* strings); /** Creates a copy of a null-terminated array of string literals. Each item from the array passed-in is added, until it encounters a null pointer, at which point it stops. */ explicit StringArray (const wchar_t* const* strings); /** Creates a copy of an array of string literals. @param strings an array of strings to add. Null pointers in the array will be treated as empty strings @param numberOfStrings how many items there are in the array */ StringArray (const wchar_t* const* strings, int numberOfStrings); #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS StringArray (const std::initializer_list& strings); #endif /** Destructor. */ ~StringArray(); /** Copies the contents of another string array into this one */ StringArray& operator= (const StringArray&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS StringArray& operator= (StringArray&&) noexcept; #endif /** Swaps the contents of this and another StringArray. */ void swapWith (StringArray&) noexcept; //============================================================================== /** Compares two arrays. Comparisons are case-sensitive. @returns true only if the other array contains exactly the same strings in the same order */ bool operator== (const StringArray&) const noexcept; /** Compares two arrays. Comparisons are case-sensitive. @returns false if the other array contains exactly the same strings in the same order */ bool operator!= (const StringArray&) const noexcept; //============================================================================== /** Returns the number of strings in the array */ inline int size() const noexcept { return strings.size(); }; /** Returns one of the strings from the array. If the index is out-of-range, an empty string is returned. Obviously the reference returned shouldn't be stored for later use, as the string it refers to may disappear when the array changes. */ const String& operator[] (int index) const noexcept; /** Returns a reference to one of the strings in the array. This lets you modify a string in-place in the array, but you must be sure that the index is in-range. */ String& getReference (int index) noexcept; /** Returns a pointer to the first String in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline String* begin() const noexcept { return strings.begin(); } /** Returns a pointer to the String which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ inline String* end() const noexcept { return strings.end(); } /** Searches for a string in the array. The comparison will be case-insensitive if the ignoreCase parameter is true. @returns true if the string is found inside the array */ bool contains (StringRef stringToLookFor, bool ignoreCase = false) const; /** Searches for a string in the array. The comparison will be case-insensitive if the ignoreCase parameter is true. @param stringToLookFor the string to try to find @param ignoreCase whether the comparison should be case-insensitive @param startIndex the first index to start searching from @returns the index of the first occurrence of the string in this array, or -1 if it isn't found. */ int indexOf (StringRef stringToLookFor, bool ignoreCase = false, int startIndex = 0) const; //============================================================================== /** Appends a string at the end of the array. */ void add (const String& stringToAdd); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS /** Appends a string at the end of the array. */ void add (String&& stringToAdd); #endif /** Inserts a string into the array. This will insert a string into the array at the given index, moving up the other elements to make room for it. If the index is less than zero or greater than the size of the array, the new string will be added to the end of the array. */ void insert (int index, const String& stringToAdd); /** Adds a string to the array as long as it's not already in there. The search can optionally be case-insensitive. */ void addIfNotAlreadyThere (const String& stringToAdd, bool ignoreCase = false); /** Replaces one of the strings in the array with another one. If the index is higher than the array's size, the new string will be added to the end of the array; if it's less than zero nothing happens. */ void set (int index, const String& newString); /** Appends some strings from another array to the end of this one. @param other the array to add @param startIndex the first element of the other array to add @param numElementsToAdd the maximum number of elements to add (if this is less than zero, they are all added) */ void addArray (const StringArray& other, int startIndex = 0, int numElementsToAdd = -1); /** Breaks up a string into tokens and adds them to this array. This will tokenise the given string using whitespace characters as the token delimiters, and will add these tokens to the end of the array. @returns the number of tokens added @see fromTokens */ int addTokens (StringRef stringToTokenise, bool preserveQuotedStrings); /** Breaks up a string into tokens and adds them to this array. This will tokenise the given string (using the string passed in to define the token delimiters), and will add these tokens to the end of the array. @param stringToTokenise the string to tokenise @param breakCharacters a string of characters, any of which will be considered to be a token delimiter. @param quoteCharacters if this string isn't empty, it defines a set of characters which are treated as quotes. Any text occurring between quotes is not broken up into tokens. @returns the number of tokens added @see fromTokens */ int addTokens (StringRef stringToTokenise, StringRef breakCharacters, StringRef quoteCharacters); /** Breaks up a string into lines and adds them to this array. This breaks a string down into lines separated by \\n or \\r\\n, and adds each line to the array. Line-break characters are omitted from the strings that are added to the array. */ int addLines (StringRef stringToBreakUp); /** Returns an array containing the tokens in a given string. This will tokenise the given string using whitespace characters as the token delimiters, and return these tokens as an array. @see addTokens */ static StringArray fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings); /** Returns an array containing the tokens in a given string. This will tokenise the given string using whitespace characters as the token delimiters, and return these tokens as an array. @param stringToTokenise the string to tokenise @param breakCharacters a string of characters, any of which will be considered to be a token delimiter. @param quoteCharacters if this string isn't empty, it defines a set of characters which are treated as quotes. Any text occurring between quotes is not broken up into tokens. @see addTokens */ static StringArray fromTokens (StringRef stringToTokenise, StringRef breakCharacters, StringRef quoteCharacters); /** Returns an array containing the lines in a given string. This breaks a string down into lines separated by \\n or \\r\\n, and returns an array containing these lines. Line-break characters are omitted from the strings that are added to the array. */ static StringArray fromLines (StringRef stringToBreakUp); //============================================================================== /** Removes all elements from the array. */ void clear(); /** Removes all elements from the array without freeing the array's allocated storage. @see clear */ void clearQuick(); /** Removes a string from the array. If the index is out-of-range, no action will be taken. */ void remove (int index); /** Finds a string in the array and removes it. This will remove the first occurrence of the given string from the array. The comparison may be case-insensitive depending on the ignoreCase parameter. */ void removeString (StringRef stringToRemove, bool ignoreCase = false); /** Removes a range of elements from the array. This will remove a set of elements, starting from the given index, and move subsequent elements down to close the gap. If the range extends beyond the bounds of the array, it will be safely clipped to the size of the array. @param startIndex the index of the first element to remove @param numberToRemove how many elements should be removed */ void removeRange (int startIndex, int numberToRemove); /** Removes any duplicated elements from the array. If any string appears in the array more than once, only the first occurrence of it will be retained. @param ignoreCase whether to use a case-insensitive comparison */ void removeDuplicates (bool ignoreCase); /** Removes empty strings from the array. @param removeWhitespaceStrings if true, strings that only contain whitespace characters will also be removed */ void removeEmptyStrings (bool removeWhitespaceStrings = true); /** Moves one of the strings to a different position. This will move the string to a specified index, shuffling along any intervening elements as required. So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. @param currentIndex the index of the value to be moved. If this isn't a valid index, then nothing will be done @param newIndex the index at which you'd like this value to end up. If this is less than zero, the value will be moved to the end of the array */ void move (int currentIndex, int newIndex) noexcept; /** Deletes any whitespace characters from the starts and ends of all the strings. */ void trim(); /** Adds numbers to the strings in the array, to make each string unique. This will add numbers to the ends of groups of similar strings. e.g. if there are two "moose" strings, they will become "moose (1)" and "moose (2)" @param ignoreCaseWhenComparing whether the comparison used is case-insensitive @param appendNumberToFirstInstance whether the first of a group of similar strings also has a number appended to it. @param preNumberString when adding a number, this string is added before the number. If you pass 0, a default string will be used, which adds brackets around the number. @param postNumberString this string is appended after any numbers that are added. If you pass 0, a default string will be used, which adds brackets around the number. */ void appendNumbersToDuplicates (bool ignoreCaseWhenComparing, bool appendNumberToFirstInstance, CharPointer_UTF8 preNumberString = CharPointer_UTF8 (nullptr), CharPointer_UTF8 postNumberString = CharPointer_UTF8 (nullptr)); //============================================================================== /** Joins the strings in the array together into one string. This will join a range of elements from the array into a string, separating them with a given string. e.g. joinIntoString (",") will turn an array of "a" "b" and "c" into "a,b,c". @param separatorString the string to insert between all the strings @param startIndex the first element to join @param numberOfElements how many elements to join together. If this is less than zero, all available elements will be used. */ String joinIntoString (StringRef separatorString, int startIndex = 0, int numberOfElements = -1) const; //============================================================================== /** Sorts the array into alphabetical order. @param ignoreCase if true, the comparisons used will be case-sensitive. */ void sort (bool ignoreCase); /** Sorts the array using extra language-aware rules to do a better job of comparing words containing spaces and numbers. @see String::compareNatural() */ void sortNatural(); //============================================================================== /** Increases the array's internal storage to hold a minimum number of elements. Calling this before adding a large known number of elements means that the array won't have to keep dynamically resizing itself as the elements are added, and it'll therefore be more efficient. */ void ensureStorageAllocated (int minNumElements); /** Reduces the amount of storage being used by the array. Arrays typically allocate slightly more storage than they need, and after removing elements, they may have quite a lot of unused space allocated. This method will reduce the amount of allocated storage to a minimum. */ void minimiseStorageOverheads(); /** This is the array holding the actual strings. This is public to allow direct access to array methods that may not already be provided by the StringArray class. */ Array strings; private: JUCE_LEAK_DETECTOR (StringArray) }; #endif // JUCE_STRINGARRAY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp000066400000000000000000000074411320201440200313270ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ StringPairArray::StringPairArray (const bool ignoreCase_) : ignoreCase (ignoreCase_) { } StringPairArray::StringPairArray (const StringPairArray& other) : keys (other.keys), values (other.values), ignoreCase (other.ignoreCase) { } StringPairArray::~StringPairArray() { } StringPairArray& StringPairArray::operator= (const StringPairArray& other) { keys = other.keys; values = other.values; return *this; } bool StringPairArray::operator== (const StringPairArray& other) const { for (int i = keys.size(); --i >= 0;) if (other [keys[i]] != values[i]) return false; return true; } bool StringPairArray::operator!= (const StringPairArray& other) const { return ! operator== (other); } const String& StringPairArray::operator[] (StringRef key) const { return values [keys.indexOf (key, ignoreCase)]; } String StringPairArray::getValue (StringRef key, const String& defaultReturnValue) const { const int i = keys.indexOf (key, ignoreCase); if (i >= 0) return values[i]; return defaultReturnValue; } bool StringPairArray::containsKey (StringRef key) const noexcept { return keys.contains (key); } void StringPairArray::set (const String& key, const String& value) { const int i = keys.indexOf (key, ignoreCase); if (i >= 0) { values.set (i, value); } else { keys.add (key); values.add (value); } } void StringPairArray::addArray (const StringPairArray& other) { for (int i = 0; i < other.size(); ++i) set (other.keys[i], other.values[i]); } void StringPairArray::clear() { keys.clear(); values.clear(); } void StringPairArray::remove (StringRef key) { remove (keys.indexOf (key, ignoreCase)); } void StringPairArray::remove (const int index) { keys.remove (index); values.remove (index); } void StringPairArray::setIgnoresCase (const bool shouldIgnoreCase) { ignoreCase = shouldIgnoreCase; } String StringPairArray::getDescription() const { String s; for (int i = 0; i < keys.size(); ++i) { s << keys[i] << " = " << values[i]; if (i < keys.size()) s << ", "; } return s; } void StringPairArray::minimiseStorageOverheads() { keys.minimiseStorageOverheads(); values.minimiseStorageOverheads(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h000066400000000000000000000143611320201440200307730ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_STRINGPAIRARRAY_H_INCLUDED #define JUCE_STRINGPAIRARRAY_H_INCLUDED //============================================================================== /** A container for holding a set of strings which are keyed by another string. @see StringArray */ class JUCE_API StringPairArray { public: //============================================================================== /** Creates an empty array */ StringPairArray (bool ignoreCaseWhenComparingKeys = true); /** Creates a copy of another array */ StringPairArray (const StringPairArray& other); /** Destructor. */ ~StringPairArray(); /** Copies the contents of another string array into this one */ StringPairArray& operator= (const StringPairArray& other); //============================================================================== /** Compares two arrays. Comparisons are case-sensitive. @returns true only if the other array contains exactly the same strings with the same keys */ bool operator== (const StringPairArray& other) const; /** Compares two arrays. Comparisons are case-sensitive. @returns false if the other array contains exactly the same strings with the same keys */ bool operator!= (const StringPairArray& other) const; //============================================================================== /** Finds the value corresponding to a key string. If no such key is found, this will just return an empty string. To check whether a given key actually exists (because it might actually be paired with an empty string), use the getAllKeys() method to obtain a list. Obviously the reference returned shouldn't be stored for later use, as the string it refers to may disappear when the array changes. @see getValue */ const String& operator[] (StringRef key) const; /** Finds the value corresponding to a key string. If no such key is found, this will just return the value provided as a default. @see operator[] */ String getValue (StringRef, const String& defaultReturnValue) const; /** Returns true if the given key exists. */ bool containsKey (StringRef key) const noexcept; /** Returns a list of all keys in the array. */ const StringArray& getAllKeys() const noexcept { return keys; } /** Returns a list of all values in the array. */ const StringArray& getAllValues() const noexcept { return values; } /** Returns the number of strings in the array */ inline int size() const noexcept { return keys.size(); }; //============================================================================== /** Adds or amends a key/value pair. If a value already exists with this key, its value will be overwritten, otherwise the key/value pair will be added to the array. */ void set (const String& key, const String& value); /** Adds the items from another array to this one. This is equivalent to using set() to add each of the pairs from the other array. */ void addArray (const StringPairArray& other); //============================================================================== /** Removes all elements from the array. */ void clear(); /** Removes a string from the array based on its key. If the key isn't found, nothing will happen. */ void remove (StringRef key); /** Removes a string from the array based on its index. If the index is out-of-range, no action will be taken. */ void remove (int index); //============================================================================== /** Indicates whether to use a case-insensitive search when looking up a key string. */ void setIgnoresCase (bool shouldIgnoreCase); //============================================================================== /** Returns a descriptive string containing the items. This is handy for dumping the contents of an array. */ String getDescription() const; //============================================================================== /** Reduces the amount of storage being used by the array. Arrays typically allocate slightly more storage than they need, and after removing elements, they may have quite a lot of unused space allocated. This method will reduce the amount of allocated storage to a minimum. */ void minimiseStorageOverheads(); private: //============================================================================== StringArray keys, values; bool ignoreCase; JUCE_LEAK_DETECTOR (StringPairArray) }; #endif // JUCE_STRINGPAIRARRAY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp000066400000000000000000000123161320201440200303430ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ static const int minNumberOfStringsForGarbageCollection = 300; static const uint32 garbageCollectionInterval = 30000; StringPool::StringPool() noexcept : lastGarbageCollectionTime (0) {} StringPool::~StringPool() {} struct StartEndString { StartEndString (String::CharPointerType s, String::CharPointerType e) noexcept : start (s), end (e) {} operator String() const { return String (start, end); } String::CharPointerType start, end; }; static int compareStrings (const String& s1, const String& s2) noexcept { return s1.compare (s2); } static int compareStrings (CharPointer_UTF8 s1, const String& s2) noexcept { return s1.compare (s2.getCharPointer()); } static int compareStrings (const StartEndString& string1, const String& string2) noexcept { String::CharPointerType s1 (string1.start), s2 (string2.getCharPointer()); for (;;) { const int c1 = s1 < string1.end ? (int) s1.getAndAdvance() : 0; const int c2 = (int) s2.getAndAdvance(); const int diff = c1 - c2; if (diff != 0) return diff < 0 ? -1 : 1; if (c1 == 0) break; } return 0; } template static String addPooledString (Array& strings, const NewStringType& newString) { int start = 0; int end = strings.size(); while (start < end) { const String& startString = strings.getReference (start); const int startComp = compareStrings (newString, startString); if (startComp == 0) return startString; const int halfway = (start + end) / 2; if (halfway == start) { if (startComp > 0) ++start; break; } const String& halfwayString = strings.getReference (halfway); const int halfwayComp = compareStrings (newString, halfwayString); if (halfwayComp == 0) return halfwayString; if (halfwayComp > 0) start = halfway; else end = halfway; } strings.insert (start, newString); return strings.getReference (start); } String StringPool::getPooledString (const char* const newString) { if (newString == nullptr || *newString == 0) return String(); const ScopedLock sl (lock); garbageCollectIfNeeded(); return addPooledString (strings, CharPointer_UTF8 (newString)); } String StringPool::getPooledString (String::CharPointerType start, String::CharPointerType end) { if (start.isEmpty() || start == end) return String(); const ScopedLock sl (lock); garbageCollectIfNeeded(); return addPooledString (strings, StartEndString (start, end)); } String StringPool::getPooledString (StringRef newString) { if (newString.isEmpty()) return String(); const ScopedLock sl (lock); garbageCollectIfNeeded(); return addPooledString (strings, newString.text); } String StringPool::getPooledString (const String& newString) { if (newString.isEmpty()) return String(); const ScopedLock sl (lock); garbageCollectIfNeeded(); return addPooledString (strings, newString); } void StringPool::garbageCollectIfNeeded() { if (strings.size() > minNumberOfStringsForGarbageCollection && Time::getApproximateMillisecondCounter() > lastGarbageCollectionTime + garbageCollectionInterval) garbageCollect(); } void StringPool::garbageCollect() { const ScopedLock sl (lock); for (int i = strings.size(); --i >= 0;) if (strings.getReference(i).getReferenceCount() == 1) strings.remove (i); lastGarbageCollectionTime = Time::getApproximateMillisecondCounter(); } StringPool& StringPool::getGlobalPool() noexcept { static StringPool pool; return pool; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h000066400000000000000000000101451320201440200300060ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_STRINGPOOL_H_INCLUDED #define JUCE_STRINGPOOL_H_INCLUDED //============================================================================== /** A StringPool holds a set of shared strings, which reduces storage overheads and improves comparison speed when dealing with many duplicate strings. When you add a string to a pool using getPooledString, it'll return a character array containing the same string. This array is owned by the pool, and the same array is returned every time a matching string is asked for. This means that it's trivial to compare two pooled strings for equality, as you can simply compare their pointers. It also cuts down on storage if you're using many copies of the same string. */ class JUCE_API StringPool { public: //============================================================================== /** Creates an empty pool. */ StringPool() noexcept; /** Destructor */ ~StringPool(); //============================================================================== /** Returns a pointer to a shared copy of the string that is passed in. The pool will always return the same String object when asked for a string that matches it. */ String getPooledString (const String& original); /** Returns a pointer to a copy of the string that is passed in. The pool will always return the same String object when asked for a string that matches it. */ String getPooledString (const char* original); /** Returns a pointer to a shared copy of the string that is passed in. The pool will always return the same String object when asked for a string that matches it. */ String getPooledString (StringRef original); /** Returns a pointer to a copy of the string that is passed in. The pool will always return the same String object when asked for a string that matches it. */ String getPooledString (String::CharPointerType start, String::CharPointerType end); //============================================================================== /** Scans the pool, and removes any strings that are unreferenced. You don't generally need to call this - it'll be called automatically when the pool grows large enough to warrant it. */ void garbageCollect(); /** Returns a shared global pool which is used for things like Identifiers, XML parsing. */ static StringPool& getGlobalPool() noexcept; private: Array strings; CriticalSection lock; uint32 lastGarbageCollectionTime; void garbageCollectIfNeeded(); JUCE_DECLARE_NON_COPYABLE (StringPool) }; #endif // JUCE_STRINGPOOL_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h000066400000000000000000000152701320201440200276150ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_STRINGREF_H_INCLUDED #define JUCE_STRINGREF_H_INCLUDED //============================================================================== /** A simple class for holding temporary references to a string literal or String. Unlike a real String object, the StringRef does not allocate any memory or take ownership of the strings you give to it - it simply holds a reference to a string that has been allocated elsewhere. The main purpose of the class is to be used instead of a const String& as the type of function arguments where the caller may pass either a string literal or a String object. This means that when the called uses a string literal, there's no need for an temporary String object to be allocated, and this cuts down overheads substantially. Because the class is simply a wrapper around a pointer, you should always pass it by value, not by reference. @code void myStringFunction1 (const String&); void myStringFunction2 (StringRef); myStringFunction1 ("abc"); // Implicitly allocates a temporary String object. myStringFunction2 ("abc"); // Much faster, as no local allocations are needed. @endcode For examples of it in use, see the XmlElement or StringArray classes. Bear in mind that there are still many cases where it's better to use an argument which is a const String&. For example if the function stores the string or needs to internally create a String from the argument, then it's better for the original argument to already be a String. @see String */ class JUCE_API StringRef { public: /** Creates a StringRef from a raw string literal. The StringRef object does NOT take ownership or copy this data, so you must ensure that the data does not change during the lifetime of the StringRef. Note that this pointer not be null! */ StringRef (const char* stringLiteral) noexcept; /** Creates a StringRef from a raw char pointer. The StringRef object does NOT take ownership or copy this data, so you must ensure that the data does not change during the lifetime of the StringRef. */ StringRef (String::CharPointerType stringLiteral) noexcept; /** Creates a StringRef from a String. The StringRef object does NOT take ownership or copy the data from the String, so you must ensure that the String is not modified or deleted during the lifetime of the StringRef. */ StringRef (const String& string) noexcept; /** Creates a StringRef pointer to an empty string. */ StringRef() noexcept; //============================================================================== /** Returns a raw pointer to the underlying string data. */ operator const String::CharPointerType::CharType*() const noexcept { return text.getAddress(); } /** Returns a pointer to the underlying string data as a char pointer object. */ operator String::CharPointerType() const noexcept { return text; } /** Returns true if the string is empty. */ bool isEmpty() const noexcept { return text.isEmpty(); } /** Returns true if the string is not empty. */ bool isNotEmpty() const noexcept { return ! text.isEmpty(); } /** Returns the number of characters in the string. */ int length() const noexcept { return (int) text.length(); } /** Retrieves a character by index. */ juce_wchar operator[] (int index) const noexcept { return text[index]; } /** Compares this StringRef with a String. */ bool operator== (const String& s) const noexcept { return text.compare (s.getCharPointer()) == 0; } /** Compares this StringRef with a String. */ bool operator!= (const String& s) const noexcept { return text.compare (s.getCharPointer()) != 0; } /** Case-sensitive comparison of two StringRefs. */ bool operator== (StringRef s) const noexcept { return text.compare (s.text) == 0; } /** Case-sensitive comparison of two StringRefs. */ bool operator!= (StringRef s) const noexcept { return text.compare (s.text) != 0; } //============================================================================== /** The text that is referenced. */ String::CharPointerType text; #if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) // Sorry, non-UTF8 people, you're unable to take advantage of StringRef, because // you've chosen a character encoding that doesn't match C++ string literals. String stringCopy; #endif }; //============================================================================== /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept; #if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); } #endif #endif // JUCE_STRINGREF_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp000066400000000000000000000176431320201440200277700ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ struct TextDiffHelpers { enum { minLengthToMatch = 3 }; struct StringRegion { StringRegion (const String& s) noexcept : text (s.getCharPointer()), start (0), length (s.length()) {} StringRegion (const String::CharPointerType t, int s, int len) noexcept : text (t), start (s), length (len) {} String::CharPointerType text; int start, length; }; static void addInsertion (TextDiff& td, const String::CharPointerType text, int index, int length) { TextDiff::Change c; c.insertedText = String (text, (size_t) length); c.start = index; c.length = length; td.changes.add (c); } static void addDeletion (TextDiff& td, int index, int length) { TextDiff::Change c; c.start = index; c.length = length; td.changes.add (c); } static void diffSkippingCommonStart (TextDiff& td, const StringRegion& a, const StringRegion& b) { String::CharPointerType sa (a.text); String::CharPointerType sb (b.text); const int maxLen = jmax (a.length, b.length); for (int i = 0; i < maxLen; ++i, ++sa, ++sb) { if (*sa != *sb) { diffRecursively (td, StringRegion (sa, a.start + i, a.length - i), StringRegion (sb, b.start + i, b.length - i)); break; } } } static void diffRecursively (TextDiff& td, const StringRegion& a, const StringRegion& b) { int indexA, indexB; const int len = findLongestCommonSubstring (a.text, a.length, b.text, b.length, indexA, indexB); if (len >= minLengthToMatch) { if (indexA > 0 && indexB > 0) diffSkippingCommonStart (td, StringRegion (a.text, a.start, indexA), StringRegion (b.text, b.start, indexB)); else if (indexA > 0) addDeletion (td, b.start, indexA); else if (indexB > 0) addInsertion (td, b.text, b.start, indexB); diffRecursively (td, StringRegion (a.text + indexA + len, a.start + indexA + len, a.length - indexA - len), StringRegion (b.text + indexB + len, b.start + indexB + len, b.length - indexB - len)); } else { if (a.length > 0) addDeletion (td, b.start, a.length); if (b.length > 0) addInsertion (td, b.text, b.start, b.length); } } static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, const String::CharPointerType b, const int lenB, int& indexInA, int& indexInB) { if (lenA == 0 || lenB == 0) return 0; HeapBlock lines; lines.calloc (2 + 2 * (size_t) lenB); int* l0 = lines; int* l1 = l0 + lenB + 1; int loopsWithoutImprovement = 0; int bestLength = 0; indexInA = indexInB = 0; for (int i = 0; i < lenA; ++i) { const juce_wchar ca = a.getAndAdvance(); String::CharPointerType b2 (b); for (int j = 0; j < lenB; ++j) { if (ca != b2.getAndAdvance()) { l1[j + 1] = 0; } else { const int len = l0[j] + 1; l1[j + 1] = len; if (len > bestLength) { loopsWithoutImprovement = 0; bestLength = len; indexInA = i; indexInB = j; } } } if (++loopsWithoutImprovement > 100) break; std::swap (l0, l1); } indexInA -= bestLength - 1; indexInB -= bestLength - 1; return bestLength; } }; TextDiff::TextDiff (const String& original, const String& target) { TextDiffHelpers::diffSkippingCommonStart (*this, original, target); } String TextDiff::appliedTo (String text) const { for (int i = 0; i < changes.size(); ++i) text = changes.getReference(i).appliedTo (text); return text; } bool TextDiff::Change::isDeletion() const noexcept { return insertedText.isEmpty(); } String TextDiff::Change::appliedTo (const String& text) const noexcept { return text.substring (0, start) + (isDeletion() ? text.substring (start + length) : (insertedText + text.substring (start))); } //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class DiffTests : public UnitTest { public: DiffTests() : UnitTest ("TextDiff class") {} static String createString (Random& r) { juce_wchar buffer[50] = { 0 }; for (int i = r.nextInt (49); --i >= 0;) { if (r.nextInt (10) == 0) { do { buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); } while (! CharPointer_UTF16::canRepresent (buffer[i])); } else buffer[i] = (juce_wchar) ('a' + r.nextInt (3)); } return CharPointer_UTF32 (buffer); } void testDiff (const String& a, const String& b) { TextDiff diff (a, b); const String result (diff.appliedTo (a)); expectEquals (result, b); } void runTest() { beginTest ("TextDiff"); Random r = getRandom(); testDiff (String::empty, String::empty); testDiff ("x", String::empty); testDiff (String::empty, "x"); testDiff ("x", "x"); testDiff ("x", "y"); testDiff ("xxx", "x"); testDiff ("x", "xxx"); for (int i = 5000; --i >= 0;) { String s (createString (r)); testDiff (s, createString (r)); testDiff (s + createString (r), s + createString (r)); } } }; static DiffTests diffTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h000066400000000000000000000070151320201440200274250ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_TEXTDIFF_H_INCLUDED #define JUCE_TEXTDIFF_H_INCLUDED /** Calculates and applies a sequence of changes to convert one text string into another. Once created, the TextDiff object contains an array of change objects, where each change can be either an insertion or a deletion. When applied in order to the original string, these changes will convert it to the target string. */ class JUCE_API TextDiff { public: /** Creates a set of diffs for converting the original string into the target. */ TextDiff (const String& original, const String& target); /** Applies this sequence of changes to the original string, producing the target string that was specified when generating them. Obviously it only makes sense to call this function with the string that was originally passed to the constructor. Any other input will produce an undefined result. */ String appliedTo (String text) const; /** Describes a change, which can be either an insertion or deletion. */ struct Change { String insertedText; /**< If this change is a deletion, this string will be empty; otherwise, it'll be the text that should be inserted at the index specified by start. */ int start; /**< Specifies the character index in a string at which text should be inserted or deleted. */ int length; /**< If this change is a deletion, this specifies the number of characters to delete. For an insertion, this is the length of the new text being inserted. */ /** Returns true if this change is a deletion, or false for an insertion. */ bool isDeletion() const noexcept; /** Returns the result of applying this change to a string. */ String appliedTo (const String& original) const noexcept; }; /** The list of changes required to perform the transformation. Applying each of these, in order, to the original string will produce the target. */ Array changes; }; #endif // JUCE_TEXTDIFF_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/000077500000000000000000000000001320201440200251745ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp000066400000000000000000000063761320201440200313040ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ ChildProcess::ChildProcess() {} ChildProcess::~ChildProcess() {} bool ChildProcess::isRunning() const { return activeProcess != nullptr && activeProcess->isRunning(); } int ChildProcess::readProcessOutput (void* dest, int numBytes) { return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0; } bool ChildProcess::kill() { return activeProcess == nullptr || activeProcess->killProcess(); } uint32 ChildProcess::getExitCode() const { return activeProcess != nullptr ? activeProcess->getExitCode() : 0; } bool ChildProcess::waitForProcessToFinish (const int timeoutMs) const { const uint32 timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs; do { if (! isRunning()) return true; } while (timeoutMs < 0 || Time::getMillisecondCounter() < timeoutTime); return false; } String ChildProcess::readAllProcessOutput() { MemoryOutputStream result; for (;;) { char buffer [512]; const int num = readProcessOutput (buffer, sizeof (buffer)); if (num <= 0) break; result.write (buffer, (size_t) num); } return result.toString(); } //============================================================================== #if JUCE_UNIT_TESTS class ChildProcessTests : public UnitTest { public: ChildProcessTests() : UnitTest ("ChildProcess") {} void runTest() { beginTest ("Child Processes"); #if JUCE_WINDOWS || JUCE_MAC || JUCE_LINUX ChildProcess p; #if JUCE_WINDOWS expect (p.start ("tasklist")); #else expect (p.start ("ls /")); #endif //String output (p.readAllProcessOutput()); //expect (output.isNotEmpty()); #endif } }; static ChildProcessTests childProcessUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h000066400000000000000000000112171320201440200307370ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CHILDPROCESS_H_INCLUDED #define JUCE_CHILDPROCESS_H_INCLUDED //============================================================================== /** Launches and monitors a child process. This class lets you launch an executable, and read its output. You can also use it to check whether the child process has finished. */ class JUCE_API ChildProcess { public: //============================================================================== /** Creates a process object. To actually launch the process, use start(). */ ChildProcess(); /** Destructor. Note that deleting this object won't terminate the child process. */ ~ChildProcess(); /** These flags are used by the start() methods. */ enum StreamFlags { wantStdOut = 1, wantStdErr = 2 }; /** Attempts to launch a child process command. The command should be the name of the executable file, followed by any arguments that are required. If the process has already been launched, this will launch it again. If a problem occurs, the method will return false. The streamFlags is a combinations of values to indicate which of the child's output streams should be read and returned by readProcessOutput(). */ bool start (const String& command, int streamFlags = wantStdOut | wantStdErr); /** Attempts to launch a child process command. The first argument should be the name of the executable file, followed by any other arguments that are needed. If the process has already been launched, this will launch it again. If a problem occurs, the method will return false. The streamFlags is a combinations of values to indicate which of the child's output streams should be read and returned by readProcessOutput(). */ bool start (const StringArray& arguments, int streamFlags = wantStdOut | wantStdErr); /** Returns true if the child process is alive. */ bool isRunning() const; /** Attempts to read some output from the child process. This will attempt to read up to the given number of bytes of data from the process. It returns the number of bytes that were actually read. */ int readProcessOutput (void* destBuffer, int numBytesToRead); /** Blocks until the process has finished, and then returns its complete output as a string. */ String readAllProcessOutput(); /** Blocks until the process is no longer running. */ bool waitForProcessToFinish (int timeoutMs) const; /** If the process has finished, this returns its exit code. */ uint32 getExitCode() const; /** Attempts to kill the child process. Returns true if it succeeded. Trying to read from the process after calling this may result in undefined behaviour. */ bool kill(); private: //============================================================================== class ActiveProcess; friend struct ContainerDeletePolicy; ScopedPointer activeProcess; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess) }; #endif // JUCE_CHILDPROCESS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h000066400000000000000000000220611320201440200314330ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_CRITICALSECTION_H_INCLUDED #define JUCE_CRITICALSECTION_H_INCLUDED //============================================================================== /** A re-entrant mutex. A CriticalSection acts as a re-entrant mutex object. The best way to lock and unlock one of these is by using RAII in the form of a local ScopedLock object - have a look through the codebase for many examples of how to do this. In almost all cases you'll want to declare your CriticalSection as a member variable. Occasionally you may want to declare one as a static variable, but in that case the usual C++ static object order-of-construction warnings should be heeded. @see ScopedLock, ScopedTryLock, ScopedUnlock, SpinLock, ReadWriteLock, Thread, InterProcessLock */ class JUCE_API CriticalSection { public: //============================================================================== /** Creates a CriticalSection object. */ CriticalSection() noexcept; /** Destructor. If the critical section is deleted whilst locked, any subsequent behaviour is unpredictable. */ ~CriticalSection() noexcept; //============================================================================== /** Acquires the lock. If the lock is already held by the caller thread, the method returns immediately. If the lock is currently held by another thread, this will wait until it becomes free. It's strongly recommended that you never call this method directly - instead use the ScopedLock class to manage the locking using an RAII pattern instead. @see exit, tryEnter, ScopedLock */ void enter() const noexcept; /** Attempts to lock this critical section without blocking. This method behaves identically to CriticalSection::enter, except that the caller thread does not wait if the lock is currently held by another thread but returns false immediately. @returns false if the lock is currently held by another thread, true otherwise. @see enter */ bool tryEnter() const noexcept; /** Releases the lock. If the caller thread hasn't got the lock, this can have unpredictable results. If the enter() method has been called multiple times by the thread, each call must be matched by a call to exit() before other threads will be allowed to take over the lock. @see enter, ScopedLock */ void exit() const noexcept; //============================================================================== /** Provides the type of scoped lock to use with a CriticalSection. */ typedef GenericScopedLock ScopedLockType; /** Provides the type of scoped unlocker to use with a CriticalSection. */ typedef GenericScopedUnlock ScopedUnlockType; /** Provides the type of scoped try-locker to use with a CriticalSection. */ typedef GenericScopedTryLock ScopedTryLockType; private: //============================================================================== #if JUCE_WINDOWS // To avoid including windows.h in the public JUCE headers, we'll just allocate // a block of memory here that's big enough to be used internally as a windows // CRITICAL_SECTION structure. #if JUCE_64BIT uint8 lock[44]; #else uint8 lock[24]; #endif #else mutable pthread_mutex_t lock; #endif JUCE_DECLARE_NON_COPYABLE (CriticalSection) }; //============================================================================== /** A class that can be used in place of a real CriticalSection object, but which doesn't perform any locking. This is currently used by some templated classes, and most compilers should manage to optimise it out of existence. @see CriticalSection, Array, OwnedArray, ReferenceCountedArray */ class JUCE_API DummyCriticalSection { public: inline DummyCriticalSection() noexcept {} inline ~DummyCriticalSection() noexcept {} inline void enter() const noexcept {} inline bool tryEnter() const noexcept { return true; } inline void exit() const noexcept {} //============================================================================== /** A dummy scoped-lock type to use with a dummy critical section. */ struct ScopedLockType { ScopedLockType (const DummyCriticalSection&) noexcept {} }; /** A dummy scoped-unlocker type to use with a dummy critical section. */ typedef ScopedLockType ScopedUnlockType; private: JUCE_DECLARE_NON_COPYABLE (DummyCriticalSection) }; //============================================================================== /** Automatically locks and unlocks a CriticalSection object. You can use a ScopedLock as a local variable to provide RAII-based locking of a CriticalSection. e.g. @code struct MyObject { CriticalSection objectLock; // assuming that this example function will be called by multiple threads void foo() { const ScopedLock myScopedLock (objectLock); // objectLock is now locked.. ...do some thread-safe work here... // ..and objectLock gets unlocked here, as myScopedLock goes out of // scope at the end of the block } }; @endcode @see CriticalSection, ScopedUnlock */ typedef CriticalSection::ScopedLockType ScopedLock; //============================================================================== /** Automatically unlocks and re-locks a CriticalSection object. This is the reverse of a ScopedLock object - instead of locking the critical section for the lifetime of this object, it unlocks it. Make sure you don't try to unlock critical sections that aren't actually locked! e.g. @code struct MyObject { CriticalSection objectLock; void foo() { { const ScopedLock myScopedLock (objectLock); // objectLock is now locked.. { ScopedUnlock myUnlocker (objectLock); // ..and now unlocked.. } // ..and now locked again.. } // ..and finally unlocked. } }; @endcode @see CriticalSection, ScopedLock */ typedef CriticalSection::ScopedUnlockType ScopedUnlock; //============================================================================== /** Automatically tries to lock and unlock a CriticalSection object. Use one of these as a local variable to control access to a CriticalSection. e.g. @code struct MyObject { CriticalSection objectLock; void foo() { const ScopedTryLock myScopedTryLock (objectLock); // Unlike using a ScopedLock, this may fail to actually get the lock, so you // must call the isLocked() method before making any assumptions.. if (myScopedTryLock.isLocked()) { ...safely do some work... } else { // If we get here, then our attempt at locking failed because another thread had already locked it.. } } }; @endcode @see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock */ typedef CriticalSection::ScopedTryLockType ScopedTryLock; #endif // JUCE_CRITICALSECTION_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h000066400000000000000000000064721320201440200312750ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_DYNAMICLIBRARY_H_INCLUDED #define JUCE_DYNAMICLIBRARY_H_INCLUDED /** Handles the opening and closing of DLLs. This class can be used to open a DLL and get some function pointers from it. Since the DLL is freed when this object is deleted, it's handy for managing library lifetimes using RAII. */ class JUCE_API DynamicLibrary { public: /** Creates an unopened DynamicLibrary object. Call open() to actually open one. */ DynamicLibrary() noexcept : handle (nullptr) {} /** */ DynamicLibrary (const String& name) : handle (nullptr) { open (name); } /** Destructor. If a library is currently open, it will be closed when this object is destroyed. */ ~DynamicLibrary() { close(); } /** Opens a DLL. The name and the method by which it gets found is of course platform-specific, and may or may not include a path, depending on the OS. If a library is already open when this method is called, it will first close the library before attempting to load the new one. @returns true if the library was successfully found and opened. */ bool open (const String& name); /** Releases the currently-open DLL, or has no effect if none was open. */ void close(); /** Tries to find a named function in the currently-open DLL, and returns a pointer to it. If no library is open, or if the function isn't found, this will return a null pointer. */ void* getFunction (const String& functionName) noexcept; /** Returns the platform-specific native library handle. You'll need to cast this to whatever is appropriate for the OS that's in use. */ void* getNativeHandle() const noexcept { return handle; } private: void* handle; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DynamicLibrary) }; #endif // JUCE_DYNAMICLIBRARY_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp000066400000000000000000000036461320201440200326630ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ HighResolutionTimer::HighResolutionTimer() { pimpl = new Pimpl (*this); } HighResolutionTimer::~HighResolutionTimer() { stopTimer(); } void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (jmax (1, periodMs)); } void HighResolutionTimer::stopTimer() { pimpl->stop(); } bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; } int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h000066400000000000000000000103431320201440200323200ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_HIGHRESOLUTIONTIMER_H_INCLUDED #define JUCE_HIGHRESOLUTIONTIMER_H_INCLUDED /** A high-resolution periodic timer. This provides accurately-timed regular callbacks. Unlike the normal Timer class, this one uses a dedicated thread, not the message thread, so is far more stable and precise. You should only use this class in situations where you really need accuracy, because unlike the normal Timer class, which is very lightweight and cheap to start/stop, the HighResolutionTimer will use far more resources, and starting/stopping it may involve launching and killing threads. @see Timer */ class JUCE_API HighResolutionTimer { protected: /** Creates a HighResolutionTimer. When created, the timer is stopped, so use startTimer() to get it going. */ HighResolutionTimer(); public: /** Destructor. */ virtual ~HighResolutionTimer(); //============================================================================== /** The user-defined callback routine that actually gets called periodically. This will be called on a dedicated timer thread, so make sure your implementation is thread-safe! It's perfectly ok to call startTimer() or stopTimer() from within this callback to change the subsequent intervals. */ virtual void hiResTimerCallback() = 0; //============================================================================== /** Starts the timer and sets the length of interval required. If the timer is already started, this will reset its counter, so the time between calling this method and the next timer callback will not be less than the interval length passed in. @param intervalInMilliseconds the interval to use (any values less than 1 will be rounded up to 1) */ void startTimer (int intervalInMilliseconds); /** Stops the timer. This method may block while it waits for pending callbacks to complete. Once it returns, no more callbacks will be made. If it is called from the timer's own thread, it will cancel the timer after the current callback returns. */ void stopTimer(); /** Checks if the timer has been started. @returns true if the timer is running. */ bool isTimerRunning() const noexcept; /** Returns the timer's interval. @returns the timer's interval in milliseconds if it's running, or 0 if it's not. */ int getTimerInterval() const noexcept; private: struct Pimpl; friend struct Pimpl; friend struct ContainerDeletePolicy; ScopedPointer pimpl; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer) }; #endif // JUCE_HIGHRESOLUTIONTIMER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h000066400000000000000000000120241320201440200316030ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_INTERPROCESSLOCK_H_INCLUDED #define JUCE_INTERPROCESSLOCK_H_INCLUDED //============================================================================== /** Acts as a critical section which processes can use to block each other. @see CriticalSection */ class JUCE_API InterProcessLock { public: //============================================================================== /** Creates a lock object. @param name a name that processes will use to identify this lock object */ explicit InterProcessLock (const String& name); /** Destructor. This will also release the lock if it's currently held by this process. */ ~InterProcessLock(); //============================================================================== /** Attempts to lock the critical section. @param timeOutMillisecs how many milliseconds to wait if the lock is already held by another process - a value of 0 will return immediately, negative values will wait forever @returns true if the lock could be gained within the timeout period, or false if the timeout expired. */ bool enter (int timeOutMillisecs = -1); /** Releases the lock if it's currently held by this process. */ void exit(); //============================================================================== /** Automatically locks and unlocks an InterProcessLock object. This works like a ScopedLock, but using an InterprocessLock rather than a CriticalSection. @see ScopedLock */ class ScopedLockType { public: //============================================================================== /** Creates a scoped lock. As soon as it is created, this will lock the InterProcessLock, and when the ScopedLockType object is deleted, the InterProcessLock will be unlocked. Note that since an InterprocessLock can fail due to errors, you should check isLocked() to make sure that the lock was successful before using it. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! Best just to use it as a local stack object, rather than creating one with the new() operator. */ explicit ScopedLockType (InterProcessLock& l) : ipLock (l) { lockWasSuccessful = l.enter(); } /** Destructor. The InterProcessLock will be unlocked when the destructor is called. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ inline ~ScopedLockType() { ipLock.exit(); } /** Returns true if the InterProcessLock was successfully locked. */ bool isLocked() const noexcept { return lockWasSuccessful; } private: //============================================================================== InterProcessLock& ipLock; bool lockWasSuccessful; JUCE_DECLARE_NON_COPYABLE (ScopedLockType) }; private: //============================================================================== class Pimpl; friend struct ContainerDeletePolicy; ScopedPointer pimpl; CriticalSection lock; String name; JUCE_DECLARE_NON_COPYABLE (InterProcessLock) }; #endif // JUCE_INTERPROCESSLOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_Process.h000066400000000000000000000141621320201440200277750ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_PROCESS_H_INCLUDED #define JUCE_PROCESS_H_INCLUDED //============================================================================== /** Represents the current executable's process. This contains methods for controlling the current application at the process-level. @see Thread, JUCEApplicationBase */ class JUCE_API Process { public: //============================================================================== enum ProcessPriority { LowPriority = 0, NormalPriority = 1, HighPriority = 2, RealtimePriority = 3 }; /** Changes the current process's priority. @param priority the process priority, where 0=low, 1=normal, 2=high, 3=realtime */ static void JUCE_CALLTYPE setPriority (const ProcessPriority priority); /** Kills the current process immediately. This is an emergency process terminator that kills the application immediately - it's intended only for use only when something goes horribly wrong. @see JUCEApplicationBase::quit */ static void JUCE_CALLTYPE terminate(); //============================================================================== /** Returns true if this application process is the one that the user is currently using. */ static bool JUCE_CALLTYPE isForegroundProcess(); /** Attempts to make the current process the active one. (This is not possible on some platforms). */ static void JUCE_CALLTYPE makeForegroundProcess(); /** Hides the application (on an OS that supports this, e.g. OSX) */ static void JUCE_CALLTYPE hide(); //============================================================================== /** Raises the current process's privilege level. Does nothing if this isn't supported by the current OS, or if process privilege level is fixed. */ static void JUCE_CALLTYPE raisePrivilege(); /** Lowers the current process's privilege level. Does nothing if this isn't supported by the current OS, or if process privilege level is fixed. */ static void JUCE_CALLTYPE lowerPrivilege(); //============================================================================== /** Returns true if this process is being hosted by a debugger. */ static bool JUCE_CALLTYPE isRunningUnderDebugger(); //============================================================================== /** Tries to launch the OS's default reader application for a given file or URL. */ static bool JUCE_CALLTYPE openDocument (const String& documentURL, const String& parameters); /** Tries to launch the OS's default email application to let the user create a message. */ static bool JUCE_CALLTYPE openEmailWithAttachments (const String& targetEmailAddress, const String& emailSubject, const String& bodyText, const StringArray& filesToAttach); #if JUCE_WINDOWS || DOXYGEN //============================================================================== /** WINDOWS ONLY - This returns the HINSTANCE of the current module. The return type is a void* to avoid being dependent on windows.h - just cast it to a HINSTANCE to use it. In a normal JUCE application, this will be automatically set to the module handle of the executable. If you've built a DLL and plan to use any JUCE messaging or windowing classes, you'll need to make sure you call the setCurrentModuleInstanceHandle() to provide the correct module handle in your DllMain() function, because the system relies on the correct instance handle when opening windows. */ static void* JUCE_CALLTYPE getCurrentModuleInstanceHandle() noexcept; /** WINDOWS ONLY - Sets a new module handle to be used by the library. The parameter type is a void* to avoid being dependent on windows.h, but it actually expects a HINSTANCE value. @see getCurrentModuleInstanceHandle() */ static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) noexcept; #endif #if JUCE_MAC || DOXYGEN //============================================================================== /** OSX ONLY - Shows or hides the OSX dock icon for this app. */ static void setDockIconVisible (bool isVisible); #endif private: Process(); JUCE_DECLARE_NON_COPYABLE (Process) }; #endif // JUCE_PROCESS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp000066400000000000000000000107561320201440200314160ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ ReadWriteLock::ReadWriteLock() noexcept : numWaitingWriters (0), numWriters (0), writerThreadId (0) { readerThreads.ensureStorageAllocated (16); } ReadWriteLock::~ReadWriteLock() noexcept { jassert (readerThreads.size() == 0); jassert (numWriters == 0); } //============================================================================== void ReadWriteLock::enterRead() const noexcept { while (! tryEnterRead()) waitEvent.wait (100); } bool ReadWriteLock::tryEnterRead() const noexcept { const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); for (int i = 0; i < readerThreads.size(); ++i) { ThreadRecursionCount& trc = readerThreads.getReference(i); if (trc.threadID == threadId) { trc.count++; return true; } } if (numWriters + numWaitingWriters == 0 || (threadId == writerThreadId && numWriters > 0)) { ThreadRecursionCount trc = { threadId, 1 }; readerThreads.add (trc); return true; } return false; } void ReadWriteLock::exitRead() const noexcept { const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); for (int i = 0; i < readerThreads.size(); ++i) { ThreadRecursionCount& trc = readerThreads.getReference(i); if (trc.threadID == threadId) { if (--(trc.count) == 0) { readerThreads.remove (i); waitEvent.signal(); } return; } } jassertfalse; // unlocking a lock that wasn't locked.. } //============================================================================== void ReadWriteLock::enterWrite() const noexcept { const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); while (! tryEnterWriteInternal (threadId)) { ++numWaitingWriters; accessLock.exit(); waitEvent.wait (100); accessLock.enter(); --numWaitingWriters; } } bool ReadWriteLock::tryEnterWrite() const noexcept { const SpinLock::ScopedLockType sl (accessLock); return tryEnterWriteInternal (Thread::getCurrentThreadId()); } bool ReadWriteLock::tryEnterWriteInternal (Thread::ThreadID threadId) const noexcept { if (readerThreads.size() + numWriters == 0 || threadId == writerThreadId || (readerThreads.size() == 1 && readerThreads.getReference(0).threadID == threadId)) { writerThreadId = threadId; ++numWriters; return true; } return false; } void ReadWriteLock::exitWrite() const noexcept { const SpinLock::ScopedLockType sl (accessLock); // check this thread actually had the lock.. jassert (numWriters > 0 && writerThreadId == Thread::getCurrentThreadId()); if (--numWriters == 0) { writerThreadId = 0; waitEvent.signal(); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h000066400000000000000000000127451320201440200310630ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_READWRITELOCK_H_INCLUDED #define JUCE_READWRITELOCK_H_INCLUDED //============================================================================== /** A critical section that allows multiple simultaneous readers. Features of this type of lock are: - Multiple readers can hold the lock at the same time, but only one writer can hold it at once. - Writers trying to gain the lock will be blocked until all readers and writers have released it - Readers trying to gain the lock while a writer is waiting to acquire it will be blocked until the writer has obtained and released it - If a thread already has a read lock and tries to obtain a write lock, it will succeed if there are no other readers - If a thread already has the write lock and tries to obtain a read lock, this will succeed. - Recursive locking is supported. @see ScopedReadLock, ScopedWriteLock, CriticalSection */ class JUCE_API ReadWriteLock { public: //============================================================================== /** Creates a ReadWriteLock object. */ ReadWriteLock() noexcept; /** Destructor. If the object is deleted whilst locked, any subsequent behaviour is undefined. */ ~ReadWriteLock() noexcept; //============================================================================== /** Locks this object for reading. Multiple threads can simultaneously lock the object for reading, but if another thread has it locked for writing, then this will block until it releases the lock. @see exitRead, ScopedReadLock */ void enterRead() const noexcept; /** Tries to lock this object for reading. Multiple threads can simultaneously lock the object for reading, but if another thread has it locked for writing, then this will fail and return false. @returns true if the lock is successfully gained. @see exitRead, ScopedReadLock */ bool tryEnterRead() const noexcept; /** Releases the read-lock. If the caller thread hasn't got the lock, this can have unpredictable results. If the enterRead() method has been called multiple times by the thread, each call must be matched by a call to exitRead() before other threads will be allowed to take over the lock. @see enterRead, ScopedReadLock */ void exitRead() const noexcept; //============================================================================== /** Locks this object for writing. This will block until any other threads that have it locked for reading or writing have released their lock. @see exitWrite, ScopedWriteLock */ void enterWrite() const noexcept; /** Tries to lock this object for writing. This is like enterWrite(), but doesn't block - it returns true if it manages to obtain the lock. @returns true if the lock is successfully gained. @see enterWrite */ bool tryEnterWrite() const noexcept; /** Releases the write-lock. If the caller thread hasn't got the lock, this can have unpredictable results. If the enterWrite() method has been called multiple times by the thread, each call must be matched by a call to exit() before other threads will be allowed to take over the lock. @see enterWrite, ScopedWriteLock */ void exitWrite() const noexcept; private: //============================================================================== SpinLock accessLock; WaitableEvent waitEvent; mutable int numWaitingWriters, numWriters; mutable Thread::ThreadID writerThreadId; struct ThreadRecursionCount { Thread::ThreadID threadID; int count; }; mutable Array readerThreads; bool tryEnterWriteInternal (Thread::ThreadID) const noexcept; JUCE_DECLARE_NON_COPYABLE (ReadWriteLock) }; #endif // JUCE_READWRITELOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h000066400000000000000000000205661320201440200304120ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SCOPEDLOCK_H_INCLUDED #define JUCE_SCOPEDLOCK_H_INCLUDED //============================================================================== /** Automatically locks and unlocks a mutex object. Use one of these as a local variable to provide RAII-based locking of a mutex. The templated class could be a CriticalSection, SpinLock, or anything else that provides enter() and exit() methods. e.g. @code CriticalSection myCriticalSection; for (;;) { const GenericScopedLock myScopedLock (myCriticalSection); // myCriticalSection is now locked ...do some stuff... // myCriticalSection gets unlocked here. } @endcode @see GenericScopedUnlock, CriticalSection, SpinLock, ScopedLock, ScopedUnlock */ template class GenericScopedLock { public: //============================================================================== /** Creates a GenericScopedLock. As soon as it is created, this will acquire the lock, and when the GenericScopedLock object is deleted, the lock will be released. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! Best just to use it as a local stack object, rather than creating one with the new() operator. */ inline explicit GenericScopedLock (const LockType& lock) noexcept : lock_ (lock) { lock.enter(); } /** Destructor. The lock will be released when the destructor is called. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ inline ~GenericScopedLock() noexcept { lock_.exit(); } private: //============================================================================== const LockType& lock_; JUCE_DECLARE_NON_COPYABLE (GenericScopedLock) }; //============================================================================== /** Automatically unlocks and re-locks a mutex object. This is the reverse of a GenericScopedLock object - instead of locking the mutex for the lifetime of this object, it unlocks it. Make sure you don't try to unlock mutexes that aren't actually locked! e.g. @code CriticalSection myCriticalSection; for (;;) { const GenericScopedLock myScopedLock (myCriticalSection); // myCriticalSection is now locked ... do some stuff with it locked .. while (xyz) { ... do some stuff with it locked .. const GenericScopedUnlock unlocker (myCriticalSection); // myCriticalSection is now unlocked for the remainder of this block, // and re-locked at the end. ...do some stuff with it unlocked ... } // myCriticalSection gets unlocked here. } @endcode @see GenericScopedLock, CriticalSection, ScopedLock, ScopedUnlock */ template class GenericScopedUnlock { public: //============================================================================== /** Creates a GenericScopedUnlock. As soon as it is created, this will unlock the CriticalSection, and when the ScopedLock object is deleted, the CriticalSection will be re-locked. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! Best just to use it as a local stack object, rather than creating one with the new() operator. */ inline explicit GenericScopedUnlock (const LockType& lock) noexcept : lock_ (lock) { lock.exit(); } /** Destructor. The CriticalSection will be unlocked when the destructor is called. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ inline ~GenericScopedUnlock() noexcept { lock_.enter(); } private: //============================================================================== const LockType& lock_; JUCE_DECLARE_NON_COPYABLE (GenericScopedUnlock) }; //============================================================================== /** Automatically locks and unlocks a mutex object. Use one of these as a local variable to provide RAII-based locking of a mutex. The templated class could be a CriticalSection, SpinLock, or anything else that provides enter() and exit() methods. e.g. @code CriticalSection myCriticalSection; for (;;) { const GenericScopedTryLock myScopedTryLock (myCriticalSection); // Unlike using a ScopedLock, this may fail to actually get the lock, so you // should test this with the isLocked() method before doing your thread-unsafe // action.. if (myScopedTryLock.isLocked()) { ...do some stuff... } else { ..our attempt at locking failed because another thread had already locked it.. } // myCriticalSection gets unlocked here (if it was locked) } @endcode @see CriticalSection::tryEnter, GenericScopedLock, GenericScopedUnlock */ template class GenericScopedTryLock { public: //============================================================================== /** Creates a GenericScopedTryLock. As soon as it is created, this will attempt to acquire the lock, and when the GenericScopedTryLock is deleted, the lock will be released (if the lock was successfully acquired). Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! Best just to use it as a local stack object, rather than creating one with the new() operator. */ inline explicit GenericScopedTryLock (const LockType& lock) noexcept : lock_ (lock), lockWasSuccessful (lock.tryEnter()) {} /** Destructor. The mutex will be unlocked (if it had been successfully locked) when the destructor is called. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ inline ~GenericScopedTryLock() noexcept { if (lockWasSuccessful) lock_.exit(); } /** Returns true if the mutex was successfully locked. */ bool isLocked() const noexcept { return lockWasSuccessful; } private: //============================================================================== const LockType& lock_; const bool lockWasSuccessful; JUCE_DECLARE_NON_COPYABLE (GenericScopedTryLock) }; #endif // JUCE_SCOPEDLOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h000066400000000000000000000063711320201440200312040ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SCOPEDREADLOCK_H_INCLUDED #define JUCE_SCOPEDREADLOCK_H_INCLUDED //============================================================================== /** Automatically locks and unlocks a ReadWriteLock object. Use one of these as a local variable to control access to a ReadWriteLock. e.g. @code ReadWriteLock myLock; for (;;) { const ScopedReadLock myScopedLock (myLock); // myLock is now locked ...do some stuff... // myLock gets unlocked here. } @endcode @see ReadWriteLock, ScopedWriteLock */ class JUCE_API ScopedReadLock { public: //============================================================================== /** Creates a ScopedReadLock. As soon as it is created, this will call ReadWriteLock::enterRead(), and when the ScopedReadLock object is deleted, the ReadWriteLock will be unlocked. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! Best just to use it as a local stack object, rather than creating one with the new() operator. */ inline explicit ScopedReadLock (const ReadWriteLock& lock) noexcept : lock_ (lock) { lock.enterRead(); } /** Destructor. The ReadWriteLock's exitRead() method will be called when the destructor is called. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ inline ~ScopedReadLock() noexcept { lock_.exitRead(); } private: //============================================================================== const ReadWriteLock& lock_; JUCE_DECLARE_NON_COPYABLE (ScopedReadLock) }; #endif // JUCE_SCOPEDREADLOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h000066400000000000000000000064021320201440200314160ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SCOPEDWRITELOCK_H_INCLUDED #define JUCE_SCOPEDWRITELOCK_H_INCLUDED //============================================================================== /** Automatically locks and unlocks a ReadWriteLock object. Use one of these as a local variable to control access to a ReadWriteLock. e.g. @code ReadWriteLock myLock; for (;;) { const ScopedWriteLock myScopedLock (myLock); // myLock is now locked ...do some stuff... // myLock gets unlocked here. } @endcode @see ReadWriteLock, ScopedReadLock */ class JUCE_API ScopedWriteLock { public: //============================================================================== /** Creates a ScopedWriteLock. As soon as it is created, this will call ReadWriteLock::enterWrite(), and when the ScopedWriteLock object is deleted, the ReadWriteLock will be unlocked. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! Best just to use it as a local stack object, rather than creating one with the new() operator. */ inline explicit ScopedWriteLock (const ReadWriteLock& lock) noexcept : lock_ (lock) { lock.enterWrite(); } /** Destructor. The ReadWriteLock's exitWrite() method will be called when the destructor is called. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ inline ~ScopedWriteLock() noexcept { lock_.exitWrite(); } private: //============================================================================== const ReadWriteLock& lock_; JUCE_DECLARE_NON_COPYABLE (ScopedWriteLock) }; #endif // JUCE_SCOPEDWRITELOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h000066400000000000000000000071701320201440200301020ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_SPINLOCK_H_INCLUDED #define JUCE_SPINLOCK_H_INCLUDED //============================================================================== /** A simple spin-lock class that can be used as a simple, low-overhead mutex for uncontended situations. Note that unlike a CriticalSection, this type of lock is not re-entrant, and may be less efficient when used it a highly contended situation, but it's very small and requires almost no initialisation. It's most appropriate for simple situations where you're only going to hold the lock for a very brief time. @see CriticalSection */ class JUCE_API SpinLock { public: inline SpinLock() noexcept {} inline ~SpinLock() noexcept {} /** Acquires the lock. This will block until the lock has been successfully acquired by this thread. Note that a SpinLock is NOT re-entrant, and is not smart enough to know whether the caller thread already has the lock - so if a thread tries to acquire a lock that it already holds, this method will never return! It's strongly recommended that you never call this method directly - instead use the ScopedLockType class to manage the locking using an RAII pattern instead. */ void enter() const noexcept; /** Attempts to acquire the lock, returning true if this was successful. */ inline bool tryEnter() const noexcept { return lock.compareAndSetBool (1, 0); } /** Releases the lock. */ inline void exit() const noexcept { jassert (lock.value == 1); // Agh! Releasing a lock that isn't currently held! lock = 0; } //============================================================================== /** Provides the type of scoped lock to use for locking a SpinLock. */ typedef GenericScopedLock ScopedLockType; /** Provides the type of scoped unlocker to use with a SpinLock. */ typedef GenericScopedUnlock ScopedUnlockType; private: //============================================================================== mutable Atomic lock; JUCE_DECLARE_NON_COPYABLE (SpinLock) }; #endif // JUCE_SPINLOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp000066400000000000000000000263541320201440200301270ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ Thread::Thread (const String& threadName_) : threadName (threadName_), threadHandle (nullptr), threadId (0), threadPriority (5), affinityMask (0), shouldExit (false) { } Thread::~Thread() { /* If your thread class's destructor has been called without first stopping the thread, that means that this partially destructed object is still performing some work - and that's probably a Bad Thing! To avoid this type of nastiness, always make sure you call stopThread() before or during your subclass's destructor. */ jassert (! isThreadRunning()); stopThread (-1); } //============================================================================== // Use a ref-counted object to hold this shared data, so that it can outlive its static // shared pointer when threads are still running during static shutdown. struct CurrentThreadHolder : public ReferenceCountedObject { CurrentThreadHolder() noexcept {} typedef ReferenceCountedObjectPtr Ptr; ThreadLocalValue value; JUCE_DECLARE_NON_COPYABLE (CurrentThreadHolder) }; static char currentThreadHolderLock [sizeof (SpinLock)]; // (statically initialised to zeros). static SpinLock* castToSpinLockWithoutAliasingWarning (void* s) { return static_cast (s); } static CurrentThreadHolder::Ptr getCurrentThreadHolder() { static CurrentThreadHolder::Ptr currentThreadHolder; SpinLock::ScopedLockType lock (*castToSpinLockWithoutAliasingWarning (currentThreadHolderLock)); if (currentThreadHolder == nullptr) currentThreadHolder = new CurrentThreadHolder(); return currentThreadHolder; } void Thread::threadEntryPoint() { const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder()); currentThreadHolder->value = this; JUCE_TRY { if (threadName.isNotEmpty()) setCurrentThreadName (threadName); if (startSuspensionEvent.wait (10000)) { jassert (getCurrentThreadId() == threadId); if (affinityMask != 0) setCurrentThreadAffinityMask (affinityMask); run(); } } JUCE_CATCH_ALL_ASSERT currentThreadHolder->value.releaseCurrentThreadStorage(); closeThreadHandle(); } // used to wrap the incoming call from the platform-specific code void JUCE_API juce_threadEntryPoint (void* userData) { static_cast (userData)->threadEntryPoint(); } //============================================================================== void Thread::startThread() { const ScopedLock sl (startStopLock); shouldExit = false; if (threadHandle == nullptr) { launchThread(); setThreadPriority (threadHandle, threadPriority); startSuspensionEvent.signal(); } } void Thread::startThread (const int priority) { const ScopedLock sl (startStopLock); if (threadHandle == nullptr) { threadPriority = priority; startThread(); } else { setPriority (priority); } } bool Thread::isThreadRunning() const { return threadHandle != nullptr; } Thread* JUCE_CALLTYPE Thread::getCurrentThread() { return getCurrentThreadHolder()->value.get(); } //============================================================================== void Thread::signalThreadShouldExit() { shouldExit = true; } bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const { // Doh! So how exactly do you expect this thread to wait for itself to stop?? jassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == 0); const uint32 timeoutEnd = Time::getMillisecondCounter() + (uint32) timeOutMilliseconds; while (isThreadRunning()) { if (timeOutMilliseconds >= 0 && Time::getMillisecondCounter() > timeoutEnd) return false; sleep (2); } return true; } bool Thread::stopThread (const int timeOutMilliseconds) { // agh! You can't stop the thread that's calling this method! How on earth // would that work?? jassert (getCurrentThreadId() != getThreadId()); const ScopedLock sl (startStopLock); if (isThreadRunning()) { signalThreadShouldExit(); notify(); if (timeOutMilliseconds != 0) waitForThreadToExit (timeOutMilliseconds); if (isThreadRunning()) { // very bad karma if this point is reached, as there are bound to be // locks and events left in silly states when a thread is killed by force.. jassertfalse; Logger::writeToLog ("!! killing thread by force !!"); killThread(); threadHandle = nullptr; threadId = 0; return false; } } return true; } //============================================================================== bool Thread::setPriority (const int newPriority) { // NB: deadlock possible if you try to set the thread prio from the thread itself, // so using setCurrentThreadPriority instead in that case. if (getCurrentThreadId() == getThreadId()) return setCurrentThreadPriority (newPriority); const ScopedLock sl (startStopLock); if ((! isThreadRunning()) || setThreadPriority (threadHandle, newPriority)) { threadPriority = newPriority; return true; } return false; } bool Thread::setCurrentThreadPriority (const int newPriority) { return setThreadPriority (0, newPriority); } void Thread::setAffinityMask (const uint32 newAffinityMask) { affinityMask = newAffinityMask; } //============================================================================== bool Thread::wait (const int timeOutMilliseconds) const { return defaultEvent.wait (timeOutMilliseconds); } void Thread::notify() const { defaultEvent.signal(); } //============================================================================== void SpinLock::enter() const noexcept { if (! tryEnter()) { for (int i = 20; --i >= 0;) if (tryEnter()) return; while (! tryEnter()) Thread::yield(); } } //============================================================================== #if JUCE_UNIT_TESTS class AtomicTests : public UnitTest { public: AtomicTests() : UnitTest ("Atomics") {} void runTest() { beginTest ("Misc"); char a1[7]; expect (numElementsInArray(a1) == 7); int a2[3]; expect (numElementsInArray(a2) == 3); expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211); expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211); expect (ByteOrder::swap ((uint64) 0x1122334455667788ULL) == 0x8877665544332211LL); beginTest ("Atomic int"); AtomicTester ::testInteger (*this); beginTest ("Atomic unsigned int"); AtomicTester ::testInteger (*this); beginTest ("Atomic int32"); AtomicTester ::testInteger (*this); beginTest ("Atomic uint32"); AtomicTester ::testInteger (*this); beginTest ("Atomic long"); AtomicTester ::testInteger (*this); beginTest ("Atomic void*"); AtomicTester ::testInteger (*this); beginTest ("Atomic int*"); AtomicTester ::testInteger (*this); beginTest ("Atomic float"); AtomicTester ::testFloat (*this); #if ! JUCE_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms beginTest ("Atomic int64"); AtomicTester ::testInteger (*this); beginTest ("Atomic uint64"); AtomicTester ::testInteger (*this); beginTest ("Atomic double"); AtomicTester ::testFloat (*this); #endif } template class AtomicTester { public: AtomicTester() {} static void testInteger (UnitTest& test) { Atomic a, b; a.set ((Type) 10); test.expect (a.value == (Type) 10); test.expect (a.get() == (Type) 10); a += (Type) 15; test.expect (a.get() == (Type) 25); a.memoryBarrier(); a -= (Type) 5; test.expect (a.get() == (Type) 20); test.expect (++a == (Type) 21); ++a; test.expect (--a == (Type) 21); test.expect (a.get() == (Type) 21); a.memoryBarrier(); testFloat (test); } static void testFloat (UnitTest& test) { Atomic a, b; a = (Type) 21; a.memoryBarrier(); /* These are some simple test cases to check the atomics - let me know if any of these assertions fail on your system! */ test.expect (a.get() == (Type) 21); test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21); test.expect (a.get() == (Type) 21); test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21); test.expect (a.get() == (Type) 101); test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200)); test.expect (a.get() == (Type) 101); test.expect (a.compareAndSetBool ((Type) 200, a.get())); test.expect (a.get() == (Type) 200); test.expect (a.exchange ((Type) 300) == (Type) 200); test.expect (a.get() == (Type) 300); b = a; test.expect (b.get() == a.get()); } }; }; static AtomicTests atomicUnitTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h000066400000000000000000000260541320201440200275710ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_THREAD_H_INCLUDED #define JUCE_THREAD_H_INCLUDED //============================================================================== /** Encapsulates a thread. Subclasses derive from Thread and implement the run() method, in which they do their business. The thread can then be started with the startThread() method and controlled with various other methods. This class also contains some thread-related static methods, such as sleep(), yield(), getCurrentThreadId() etc. @see CriticalSection, WaitableEvent, Process, ThreadWithProgressWindow, MessageManagerLock */ class JUCE_API Thread { public: //============================================================================== /** Creates a thread. When first created, the thread is not running. Use the startThread() method to start it. */ explicit Thread (const String& threadName); /** Destructor. You must never attempt to delete a Thread object while it's still running - always call stopThread() and make sure your thread has stopped before deleting the object. Failing to do so will throw an assertion, and put you firmly into undefined behaviour territory. */ virtual ~Thread(); //============================================================================== /** Must be implemented to perform the thread's actual code. Remember that the thread must regularly check the threadShouldExit() method whilst running, and if this returns true it should return from the run() method as soon as possible to avoid being forcibly killed. @see threadShouldExit, startThread */ virtual void run() = 0; //============================================================================== // Thread control functions.. /** Starts the thread running. This will cause the thread's run() method to be called by a new thread. If this thread is already running, startThread() won't do anything. @see stopThread */ void startThread(); /** Starts the thread with a given priority. Launches the thread with a given priority, where 0 = lowest, 10 = highest. If the thread is already running, its priority will be changed. @see startThread, setPriority */ void startThread (int priority); /** Attempts to stop the thread running. This method will cause the threadShouldExit() method to return true and call notify() in case the thread is currently waiting. Hopefully the thread will then respond to this by exiting cleanly, and the stopThread method will wait for a given time-period for this to happen. If the thread is stuck and fails to respond after the time-out, it gets forcibly killed, which is a very bad thing to happen, as it could still be holding locks, etc. which are needed by other parts of your program. @param timeOutMilliseconds The number of milliseconds to wait for the thread to finish before killing it by force. A negative value in here will wait forever. @returns true if the thread was cleanly stopped before the timeout, or false if it had to be killed by force. @see signalThreadShouldExit, threadShouldExit, waitForThreadToExit, isThreadRunning */ bool stopThread (int timeOutMilliseconds); //============================================================================== /** Returns true if the thread is currently active */ bool isThreadRunning() const; /** Sets a flag to tell the thread it should stop. Calling this means that the threadShouldExit() method will then return true. The thread should be regularly checking this to see whether it should exit. If your thread makes use of wait(), you might want to call notify() after calling this method, to interrupt any waits that might be in progress, and allow it to reach a point where it can exit. @see threadShouldExit @see waitForThreadToExit */ void signalThreadShouldExit(); /** Checks whether the thread has been told to stop running. Threads need to check this regularly, and if it returns true, they should return from their run() method at the first possible opportunity. @see signalThreadShouldExit */ inline bool threadShouldExit() const { return shouldExit; } /** Waits for the thread to stop. This will waits until isThreadRunning() is false or until a timeout expires. @param timeOutMilliseconds the time to wait, in milliseconds. If this value is less than zero, it will wait forever. @returns true if the thread exits, or false if the timeout expires first. */ bool waitForThreadToExit (int timeOutMilliseconds) const; //============================================================================== /** Changes the thread's priority. May return false if for some reason the priority can't be changed. @param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority of 5 is normal. */ bool setPriority (int priority); /** Changes the priority of the caller thread. Similar to setPriority(), but this static method acts on the caller thread. May return false if for some reason the priority can't be changed. @see setPriority */ static bool setCurrentThreadPriority (int priority); //============================================================================== /** Sets the affinity mask for the thread. This will only have an effect next time the thread is started - i.e. if the thread is already running when called, it'll have no effect. @see setCurrentThreadAffinityMask */ void setAffinityMask (uint32 affinityMask); /** Changes the affinity mask for the caller thread. This will change the affinity mask for the thread that calls this static method. @see setAffinityMask */ static void JUCE_CALLTYPE setCurrentThreadAffinityMask (uint32 affinityMask); //============================================================================== // this can be called from any thread that needs to pause.. static void JUCE_CALLTYPE sleep (int milliseconds); /** Yields the calling thread's current time-slot. */ static void JUCE_CALLTYPE yield(); //============================================================================== /** Makes the thread wait for a notification. This puts the thread to sleep until either the timeout period expires, or another thread calls the notify() method to wake it up. A negative time-out value means that the method will wait indefinitely. @returns true if the event has been signalled, false if the timeout expires. */ bool wait (int timeOutMilliseconds) const; /** Wakes up the thread. If the thread has called the wait() method, this will wake it up. @see wait */ void notify() const; //============================================================================== /** A value type used for thread IDs. @see getCurrentThreadId(), getThreadId() */ typedef void* ThreadID; /** Returns an id that identifies the caller thread. To find the ID of a particular thread object, use getThreadId(). @returns a unique identifier that identifies the calling thread. @see getThreadId */ static ThreadID JUCE_CALLTYPE getCurrentThreadId(); /** Finds the thread object that is currently running. Note that the main UI thread (or other non-Juce threads) don't have a Thread object associated with them, so this will return nullptr. */ static Thread* JUCE_CALLTYPE getCurrentThread(); /** Returns the ID of this thread. That means the ID of this thread object - not of the thread that's calling the method. This can change when the thread is started and stopped, and will be invalid if the thread's not actually running. @see getCurrentThreadId */ ThreadID getThreadId() const noexcept { return threadId; } /** Returns the name of the thread. This is the name that gets set in the constructor. */ const String& getThreadName() const { return threadName; } /** Changes the name of the caller thread. Different OSes may place different length or content limits on this name. */ static void JUCE_CALLTYPE setCurrentThreadName (const String& newThreadName); private: //============================================================================== const String threadName; void* volatile threadHandle; ThreadID threadId; CriticalSection startStopLock; WaitableEvent startSuspensionEvent, defaultEvent; int threadPriority; uint32 affinityMask; bool volatile shouldExit; #ifndef DOXYGEN friend void JUCE_API juce_threadEntryPoint (void*); #endif void launchThread(); void closeThreadHandle(); void killThread(); void threadEntryPoint(); static bool setThreadPriority (void*, int); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread) }; #endif // JUCE_THREAD_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h000066400000000000000000000163531320201440200315420ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_THREADLOCALVALUE_H_INCLUDED #define JUCE_THREADLOCALVALUE_H_INCLUDED // (NB: on win32, native thread-locals aren't possible in a dynamically loaded DLL in XP). #if ! ((JUCE_MSVC && (JUCE_64BIT || ! defined (JucePlugin_PluginCode))) \ || (JUCE_MAC && JUCE_CLANG && defined (MAC_OS_X_VERSION_10_7) \ && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)) #define JUCE_NO_COMPILER_THREAD_LOCAL 1 #endif //============================================================================== /** Provides cross-platform support for thread-local objects. This class holds an internal list of objects of the templated type, keeping an instance for each thread that requests one. The first time a thread attempts to access its value, an object is created and added to the list for that thread. Typically, you'll probably want to create a static instance of a ThreadLocalValue object, or hold one within a singleton. The templated class for your value must be a primitive type, or a simple POD struct. When a thread no longer needs to use its value, it can call releaseCurrentThreadStorage() to allow the storage to be re-used by another thread. If a thread exits without calling this method, the object storage will be left allocated until the ThreadLocalValue object is deleted. */ template class ThreadLocalValue { public: /** */ ThreadLocalValue() noexcept { } /** Destructor. When this object is deleted, all the value objects for all threads will be deleted. */ ~ThreadLocalValue() { #if JUCE_NO_COMPILER_THREAD_LOCAL for (ObjectHolder* o = first.value; o != nullptr;) { ObjectHolder* const next = o->next; delete o; o = next; } #endif } /** Returns a reference to this thread's instance of the value. Note that the first time a thread tries to access the value, an instance of the value object will be created - so if your value's class has a non-trivial constructor, be aware that this method could invoke it. */ Type& operator*() const noexcept { return get(); } /** Returns a pointer to this thread's instance of the value. Note that the first time a thread tries to access the value, an instance of the value object will be created - so if your value's class has a non-trivial constructor, be aware that this method could invoke it. */ operator Type*() const noexcept { return &get(); } /** Accesses a method or field of the value object. Note that the first time a thread tries to access the value, an instance of the value object will be created - so if your value's class has a non-trivial constructor, be aware that this method could invoke it. */ Type* operator->() const noexcept { return &get(); } /** Assigns a new value to the thread-local object. */ ThreadLocalValue& operator= (const Type& newValue) { get() = newValue; return *this; } /** Returns a reference to this thread's instance of the value. Note that the first time a thread tries to access the value, an instance of the value object will be created - so if your value's class has a non-trivial constructor, be aware that this method could invoke it. */ Type& get() const noexcept { #if JUCE_NO_COMPILER_THREAD_LOCAL const Thread::ThreadID threadId = Thread::getCurrentThreadId(); for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) if (o->threadId == threadId) return o->object; for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) { if (o->threadId == nullptr) { { SpinLock::ScopedLockType sl (lock); if (o->threadId != nullptr) continue; o->threadId = threadId; } o->object = Type(); return o->object; } } ObjectHolder* const newObject = new ObjectHolder (threadId); do { newObject->next = first.get(); } while (! first.compareAndSetBool (newObject, newObject->next)); return newObject->object; #elif JUCE_MAC static __thread Type object; return object; #elif JUCE_MSVC static __declspec(thread) Type object; return object; #endif } /** Called by a thread before it terminates, to allow this class to release any storage associated with the thread. */ void releaseCurrentThreadStorage() { #if JUCE_NO_COMPILER_THREAD_LOCAL const Thread::ThreadID threadId = Thread::getCurrentThreadId(); for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) { if (o->threadId == threadId) { SpinLock::ScopedLockType sl (lock); o->threadId = nullptr; } } #endif } private: //============================================================================== #if JUCE_NO_COMPILER_THREAD_LOCAL struct ObjectHolder { ObjectHolder (const Thread::ThreadID& tid) : threadId (tid), next (nullptr), object() {} Thread::ThreadID threadId; ObjectHolder* next; Type object; JUCE_DECLARE_NON_COPYABLE (ObjectHolder) }; mutable Atomic first; SpinLock lock; #endif JUCE_DECLARE_NON_COPYABLE (ThreadLocalValue) }; #endif // JUCE_THREADLOCALVALUE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp000066400000000000000000000242501320201440200307520ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ class ThreadPool::ThreadPoolThread : public Thread { public: ThreadPoolThread (ThreadPool& p) : Thread ("Pool"), currentJob (nullptr), pool (p) { } void run() override { while (! threadShouldExit()) if (! pool.runNextJob (*this)) wait (500); } ThreadPoolJob* volatile currentJob; ThreadPool& pool; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread) }; //============================================================================== ThreadPoolJob::ThreadPoolJob (const String& name) : jobName (name), pool (nullptr), shouldStop (false), isActive (false), shouldBeDeleted (false) { } ThreadPoolJob::~ThreadPoolJob() { // you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob() // to remove it first! jassert (pool == nullptr || ! pool->contains (this)); } String ThreadPoolJob::getJobName() const { return jobName; } void ThreadPoolJob::setJobName (const String& newName) { jobName = newName; } void ThreadPoolJob::signalJobShouldExit() { shouldStop = true; } ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob() { if (ThreadPool::ThreadPoolThread* t = dynamic_cast (Thread::getCurrentThread())) return t->currentJob; return nullptr; } //============================================================================== ThreadPool::ThreadPool (const int numThreads) { jassert (numThreads > 0); // not much point having a pool without any threads! createThreads (numThreads); } ThreadPool::ThreadPool() { createThreads (SystemStats::getNumCpus()); } ThreadPool::~ThreadPool() { removeAllJobs (true, 5000); stopThreads(); } void ThreadPool::createThreads (int numThreads) { for (int i = jmax (1, numThreads); --i >= 0;) threads.add (new ThreadPoolThread (*this)); for (int i = threads.size(); --i >= 0;) threads.getUnchecked(i)->startThread(); } void ThreadPool::stopThreads() { for (int i = threads.size(); --i >= 0;) threads.getUnchecked(i)->signalThreadShouldExit(); for (int i = threads.size(); --i >= 0;) threads.getUnchecked(i)->stopThread (500); } void ThreadPool::addJob (ThreadPoolJob* const job, const bool deleteJobWhenFinished) { jassert (job != nullptr); jassert (job->pool == nullptr); if (job->pool == nullptr) { job->pool = this; job->shouldStop = false; job->isActive = false; job->shouldBeDeleted = deleteJobWhenFinished; { const ScopedLock sl (lock); jobs.add (job); } for (int i = threads.size(); --i >= 0;) threads.getUnchecked(i)->notify(); } } int ThreadPool::getNumJobs() const { return jobs.size(); } ThreadPoolJob* ThreadPool::getJob (const int index) const { const ScopedLock sl (lock); return jobs [index]; } bool ThreadPool::contains (const ThreadPoolJob* const job) const { const ScopedLock sl (lock); return jobs.contains (const_cast (job)); } bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const { const ScopedLock sl (lock); return jobs.contains (const_cast (job)) && job->isActive; } bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, const int timeOutMs) const { if (job != nullptr) { const uint32 start = Time::getMillisecondCounter(); while (contains (job)) { if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs) return false; jobFinishedSignal.wait (2); } } return true; } bool ThreadPool::removeJob (ThreadPoolJob* const job, const bool interruptIfRunning, const int timeOutMs) { bool dontWait = true; OwnedArray deletionList; if (job != nullptr) { const ScopedLock sl (lock); if (jobs.contains (job)) { if (job->isActive) { if (interruptIfRunning) job->signalJobShouldExit(); dontWait = false; } else { jobs.removeFirstMatchingValue (job); addToDeleteList (deletionList, job); } } } return dontWait || waitForJobToFinish (job, timeOutMs); } bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs, ThreadPool::JobSelector* const selectedJobsToRemove) { Array jobsToWaitFor; { OwnedArray deletionList; { const ScopedLock sl (lock); for (int i = jobs.size(); --i >= 0;) { ThreadPoolJob* const job = jobs.getUnchecked(i); if (selectedJobsToRemove == nullptr || selectedJobsToRemove->isJobSuitable (job)) { if (job->isActive) { jobsToWaitFor.add (job); if (interruptRunningJobs) job->signalJobShouldExit(); } else { jobs.remove (i); addToDeleteList (deletionList, job); } } } } } const uint32 start = Time::getMillisecondCounter(); for (;;) { for (int i = jobsToWaitFor.size(); --i >= 0;) { ThreadPoolJob* const job = jobsToWaitFor.getUnchecked (i); if (! isJobRunning (job)) jobsToWaitFor.remove (i); } if (jobsToWaitFor.size() == 0) break; if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs) return false; jobFinishedSignal.wait (20); } return true; } StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) const { StringArray s; const ScopedLock sl (lock); for (int i = 0; i < jobs.size(); ++i) { const ThreadPoolJob* const job = jobs.getUnchecked(i); if (job->isActive || ! onlyReturnActiveJobs) s.add (job->getJobName()); } return s; } bool ThreadPool::setThreadPriorities (const int newPriority) { bool ok = true; for (int i = threads.size(); --i >= 0;) if (! threads.getUnchecked(i)->setPriority (newPriority)) ok = false; return ok; } ThreadPoolJob* ThreadPool::pickNextJobToRun() { OwnedArray deletionList; { const ScopedLock sl (lock); for (int i = 0; i < jobs.size(); ++i) { ThreadPoolJob* job = jobs[i]; if (job != nullptr && ! job->isActive) { if (job->shouldStop) { jobs.remove (i); addToDeleteList (deletionList, job); --i; continue; } job->isActive = true; return job; } } } return nullptr; } bool ThreadPool::runNextJob (ThreadPoolThread& thread) { if (ThreadPoolJob* const job = pickNextJobToRun()) { ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished; thread.currentJob = job; JUCE_TRY { result = job->runJob(); } JUCE_CATCH_ALL_ASSERT thread.currentJob = nullptr; OwnedArray deletionList; { const ScopedLock sl (lock); if (jobs.contains (job)) { job->isActive = false; if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop) { jobs.removeFirstMatchingValue (job); addToDeleteList (deletionList, job); jobFinishedSignal.signal(); } else { // move the job to the end of the queue if it wants another go jobs.move (jobs.indexOf (job), -1); } } } return true; } return false; } void ThreadPool::addToDeleteList (OwnedArray& deletionList, ThreadPoolJob* const job) const { job->shouldStop = true; job->pool = nullptr; if (job->shouldBeDeleted) deletionList.add (job); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h000066400000000000000000000321521320201440200304170ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_THREADPOOL_H_INCLUDED #define JUCE_THREADPOOL_H_INCLUDED class ThreadPool; class ThreadPoolThread; //============================================================================== /** A task that is executed by a ThreadPool object. A ThreadPool keeps a list of ThreadPoolJob objects which are executed by its threads. The runJob() method needs to be implemented to do the task, and if the code that does the work takes a significant time to run, it must keep checking the shouldExit() method to see if something is trying to interrupt the job. If shouldExit() returns true, the runJob() method must return immediately. @see ThreadPool, Thread */ class JUCE_API ThreadPoolJob { public: //============================================================================== /** Creates a thread pool job object. After creating your job, add it to a thread pool with ThreadPool::addJob(). */ explicit ThreadPoolJob (const String& name); /** Destructor. */ virtual ~ThreadPoolJob(); //============================================================================== /** Returns the name of this job. @see setJobName */ String getJobName() const; /** Changes the job's name. @see getJobName */ void setJobName (const String& newName); //============================================================================== /** These are the values that can be returned by the runJob() method. */ enum JobStatus { jobHasFinished = 0, /**< indicates that the job has finished and can be removed from the pool. */ jobNeedsRunningAgain /**< indicates that the job would like to be called again when a thread is free. */ }; /** Peforms the actual work that this job needs to do. Your subclass must implement this method, in which is does its work. If the code in this method takes a significant time to run, it must repeatedly check the shouldExit() method to see if something is trying to interrupt the job. If shouldExit() ever returns true, the runJob() method must return immediately. If this method returns jobHasFinished, then the job will be removed from the pool immediately. If it returns jobNeedsRunningAgain, then the job will be left in the pool and will get a chance to run again as soon as a thread is free. @see shouldExit() */ virtual JobStatus runJob() = 0; //============================================================================== /** Returns true if this job is currently running its runJob() method. */ bool isRunning() const noexcept { return isActive; } /** Returns true if something is trying to interrupt this job and make it stop. Your runJob() method must call this whenever it gets a chance, and if it ever returns true, the runJob() method must return immediately. @see signalJobShouldExit() */ bool shouldExit() const noexcept { return shouldStop; } /** Calling this will cause the shouldExit() method to return true, and the job should (if it's been implemented correctly) stop as soon as possible. @see shouldExit() */ void signalJobShouldExit(); //============================================================================== /** If the calling thread is being invoked inside a runJob() method, this will return the ThreadPoolJob that it belongs to. */ static ThreadPoolJob* getCurrentThreadPoolJob(); //============================================================================== private: friend class ThreadPool; friend class ThreadPoolThread; String jobName; ThreadPool* pool; bool shouldStop, isActive, shouldBeDeleted; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolJob) }; //============================================================================== /** A set of threads that will run a list of jobs. When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method will be called by the next pooled thread that becomes free. @see ThreadPoolJob, Thread */ class JUCE_API ThreadPool { public: //============================================================================== /** Creates a thread pool. Once you've created a pool, you can give it some jobs by calling addJob(). @param numberOfThreads the number of threads to run. These will be started immediately, and will run until the pool is deleted. */ ThreadPool (int numberOfThreads); /** Creates a thread pool with one thread per CPU core. Once you've created a pool, you can give it some jobs by calling addJob(). If you want to specify the number of threads, use the other constructor; this one creates a pool which has one thread for each CPU core. @see SystemStats::getNumCpus() */ ThreadPool(); /** Destructor. This will attempt to remove all the jobs before deleting, but if you want to specify a timeout, you should call removeAllJobs() explicitly before deleting the pool. */ ~ThreadPool(); //============================================================================== /** A callback class used when you need to select which ThreadPoolJob objects are suitable for some kind of operation. @see ThreadPool::removeAllJobs */ class JUCE_API JobSelector { public: virtual ~JobSelector() {} /** Should return true if the specified thread matches your criteria for whatever operation that this object is being used for. Any implementation of this method must be extremely fast and thread-safe! */ virtual bool isJobSuitable (ThreadPoolJob* job) = 0; }; //============================================================================== /** Adds a job to the queue. Once a job has been added, then the next time a thread is free, it will run the job's ThreadPoolJob::runJob() method. Depending on the return value of the runJob() method, the pool will either remove the job from the pool or add it to the back of the queue to be run again. If deleteJobWhenFinished is true, then the job object will be owned and deleted by the pool when not needed - if you do this, make sure that your object's destructor is thread-safe. If deleteJobWhenFinished is false, the pointer will be used but not deleted, and the caller is responsible for making sure the object is not deleted before it has been removed from the pool. */ void addJob (ThreadPoolJob* job, bool deleteJobWhenFinished); /** Tries to remove a job from the pool. If the job isn't yet running, this will simply remove it. If it is running, it will wait for it to finish. If the timeout period expires before the job finishes running, then the job will be left in the pool and this will return false. It returns true if the job is successfully stopped and removed. @param job the job to remove @param interruptIfRunning if true, then if the job is currently busy, its ThreadPoolJob::signalJobShouldExit() method will be called to try to interrupt it. If false, then if the job will be allowed to run until it stops normally (or the timeout expires) @param timeOutMilliseconds the length of time this method should wait for the job to finish before giving up and returning false */ bool removeJob (ThreadPoolJob* job, bool interruptIfRunning, int timeOutMilliseconds); /** Tries to remove all jobs from the pool. @param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit() methods called to try to interrupt them @param timeOutMilliseconds the length of time this method should wait for all the jobs to finish before giving up and returning false @param selectedJobsToRemove if this is non-zero, the JobSelector object is asked to decide which jobs should be removed. If it is zero, all jobs are removed @returns true if all jobs are successfully stopped and removed; false if the timeout period expires while waiting for one or more jobs to stop */ bool removeAllJobs (bool interruptRunningJobs, int timeOutMilliseconds, JobSelector* selectedJobsToRemove = nullptr); /** Returns the number of jobs currently running or queued. */ int getNumJobs() const; /** Returns one of the jobs in the queue. Note that this can be a very volatile list as jobs might be continuously getting shifted around in the list, and this method may return nullptr if the index is currently out-of-range. */ ThreadPoolJob* getJob (int index) const; /** Returns true if the given job is currently queued or running. @see isJobRunning() */ bool contains (const ThreadPoolJob* job) const; /** Returns true if the given job is currently being run by a thread. */ bool isJobRunning (const ThreadPoolJob* job) const; /** Waits until a job has finished running and has been removed from the pool. This will wait until the job is no longer in the pool - i.e. until its runJob() method returns ThreadPoolJob::jobHasFinished. If the timeout period expires before the job finishes, this will return false; it returns true if the job has finished successfully. */ bool waitForJobToFinish (const ThreadPoolJob* job, int timeOutMilliseconds) const; /** Returns a list of the names of all the jobs currently running or queued. If onlyReturnActiveJobs is true, only the ones currently running are returned. */ StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const; /** Changes the priority of all the threads. This will call Thread::setPriority() for each thread in the pool. May return false if for some reason the priority can't be changed. */ bool setThreadPriorities (int newPriority); private: //============================================================================== Array jobs; class ThreadPoolThread; friend class ThreadPoolJob; friend class ThreadPoolThread; friend struct ContainerDeletePolicy; OwnedArray threads; CriticalSection lock; WaitableEvent jobFinishedSignal; bool runNextJob (ThreadPoolThread&); ThreadPoolJob* pickNextJobToRun(); void addToDeleteList (OwnedArray&, ThreadPoolJob*) const; void createThreads (int numThreads); void stopThreads(); // Note that this method has changed, and no longer has a parameter to indicate // whether the jobs should be deleted - see the new method for details. void removeAllJobs (bool, int, bool); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPool) }; #endif // JUCE_THREADPOOL_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp000066400000000000000000000122701320201440200317160ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ TimeSliceThread::TimeSliceThread (const String& name) : Thread (name), clientBeingCalled (nullptr) { } TimeSliceThread::~TimeSliceThread() { stopThread (2000); } //============================================================================== void TimeSliceThread::addTimeSliceClient (TimeSliceClient* const client, int millisecondsBeforeStarting) { if (client != nullptr) { const ScopedLock sl (listLock); client->nextCallTime = Time::getCurrentTime() + RelativeTime::milliseconds (millisecondsBeforeStarting); clients.addIfNotAlreadyThere (client); notify(); } } void TimeSliceThread::removeTimeSliceClient (TimeSliceClient* const client) { const ScopedLock sl1 (listLock); // if there's a chance we're in the middle of calling this client, we need to // also lock the outer lock.. if (clientBeingCalled == client) { const ScopedUnlock ul (listLock); // unlock first to get the order right.. const ScopedLock sl2 (callbackLock); const ScopedLock sl3 (listLock); clients.removeFirstMatchingValue (client); } else { clients.removeFirstMatchingValue (client); } } void TimeSliceThread::moveToFrontOfQueue (TimeSliceClient* client) { const ScopedLock sl (listLock); if (clients.contains (client)) { client->nextCallTime = Time::getCurrentTime(); notify(); } } int TimeSliceThread::getNumClients() const { return clients.size(); } TimeSliceClient* TimeSliceThread::getClient (const int i) const { const ScopedLock sl (listLock); return clients [i]; } //============================================================================== TimeSliceClient* TimeSliceThread::getNextClient (int index) const { Time soonest; TimeSliceClient* client = nullptr; for (int i = clients.size(); --i >= 0;) { TimeSliceClient* const c = clients.getUnchecked ((i + index) % clients.size()); if (client == nullptr || c->nextCallTime < soonest) { client = c; soonest = c->nextCallTime; } } return client; } void TimeSliceThread::run() { int index = 0; while (! threadShouldExit()) { int timeToWait = 500; { Time nextClientTime; { const ScopedLock sl2 (listLock); index = clients.size() > 0 ? ((index + 1) % clients.size()) : 0; if (TimeSliceClient* const firstClient = getNextClient (index)) nextClientTime = firstClient->nextCallTime; } const Time now (Time::getCurrentTime()); if (nextClientTime > now) { timeToWait = (int) jmin ((int64) 500, (nextClientTime - now).inMilliseconds()); } else { timeToWait = index == 0 ? 1 : 0; const ScopedLock sl (callbackLock); { const ScopedLock sl2 (listLock); clientBeingCalled = getNextClient (index); } if (clientBeingCalled != nullptr) { const int msUntilNextCall = clientBeingCalled->useTimeSlice(); const ScopedLock sl2 (listLock); if (msUntilNextCall >= 0) clientBeingCalled->nextCallTime = now + RelativeTime::milliseconds (msUntilNextCall); else clients.removeFirstMatchingValue (clientBeingCalled); clientBeingCalled = nullptr; } } } if (timeToWait > 0) wait (timeToWait); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h000066400000000000000000000134041320201440200313630ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_TIMESLICETHREAD_H_INCLUDED #define JUCE_TIMESLICETHREAD_H_INCLUDED class TimeSliceThread; //============================================================================== /** Used by the TimeSliceThread class. To register your class with a TimeSliceThread, derive from this class and use the TimeSliceThread::addTimeSliceClient() method to add it to the list. Make sure you always call TimeSliceThread::removeTimeSliceClient() before deleting your client! @see TimeSliceThread */ class JUCE_API TimeSliceClient { public: /** Destructor. */ virtual ~TimeSliceClient() {} /** Called back by a TimeSliceThread. When you register this class with it, a TimeSliceThread will repeatedly call this method. The implementation of this method should use its time-slice to do something that's quick - never block for longer than absolutely necessary. @returns Your method should return the number of milliseconds which it would like to wait before being called again. Returning 0 will make the thread call again as soon as possible (after possibly servicing other busy clients). If you return a value below zero, your client will be removed from the list of clients, and won't be called again. The value you specify isn't a guaranteee, and is only used as a hint by the thread - the actual time before the next callback may be more or less than specified. You can force the TimeSliceThread to wake up and poll again immediately by calling its notify() method. */ virtual int useTimeSlice() = 0; private: friend class TimeSliceThread; Time nextCallTime; }; //============================================================================== /** A thread that keeps a list of clients, and calls each one in turn, giving them all a chance to run some sort of short task. @see TimeSliceClient, Thread */ class JUCE_API TimeSliceThread : public Thread { public: //============================================================================== /** Creates a TimeSliceThread. When first created, the thread is not running. Use the startThread() method to start it. */ explicit TimeSliceThread (const String& threadName); /** Destructor. Deleting a Thread object that is running will only give the thread a brief opportunity to stop itself cleanly, so it's recommended that you should always call stopThread() with a decent timeout before deleting, to avoid the thread being forcibly killed (which is a Bad Thing). */ ~TimeSliceThread(); //============================================================================== /** Adds a client to the list. The client's callbacks will start after the number of milliseconds specified by millisecondsBeforeStarting (and this may happen before this method has returned). */ void addTimeSliceClient (TimeSliceClient* client, int millisecondsBeforeStarting = 0); /** Removes a client from the list. This method will make sure that all callbacks to the client have completely finished before the method returns. */ void removeTimeSliceClient (TimeSliceClient* client); /** If the given client is waiting in the queue, it will be moved to the front and given a time-slice as soon as possible. If the specified client has not been added, nothing will happen. */ void moveToFrontOfQueue (TimeSliceClient* client); /** Returns the number of registered clients. */ int getNumClients() const; /** Returns one of the registered clients. */ TimeSliceClient* getClient (int index) const; //============================================================================== #ifndef DOXYGEN void run() override; #endif //============================================================================== private: CriticalSection callbackLock, listLock; Array clients; TimeSliceClient* clientBeingCalled; TimeSliceClient* getNextClient (int index) const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimeSliceThread) }; #endif // JUCE_TIMESLICETHREAD_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h000066400000000000000000000115351320201440200311120ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_WAITABLEEVENT_H_INCLUDED #define JUCE_WAITABLEEVENT_H_INCLUDED //============================================================================== /** Allows threads to wait for events triggered by other threads. A thread can call wait() on a WaitableObject, and this will suspend the calling thread until another thread wakes it up by calling the signal() method. */ class JUCE_API WaitableEvent { public: //============================================================================== /** Creates a WaitableEvent object. The object is initially in an unsignalled state. @param manualReset If this is false, the event will be reset automatically when the wait() method is called. If manualReset is true, then once the event is signalled, the only way to reset it will be by calling the reset() method. */ explicit WaitableEvent (bool manualReset = false) noexcept; /** Destructor. If other threads are waiting on this object when it gets deleted, this can cause nasty errors, so be careful! */ ~WaitableEvent() noexcept; //============================================================================== /** Suspends the calling thread until the event has been signalled. This will wait until the object's signal() method is called by another thread, or until the timeout expires. After the event has been signalled, this method will return true and if manualReset was set to false in the WaitableEvent's constructor, then the event will be reset. @param timeOutMilliseconds the maximum time to wait, in milliseconds. A negative value will cause it to wait forever. @returns true if the object has been signalled, false if the timeout expires first. @see signal, reset */ bool wait (int timeOutMilliseconds = -1) const noexcept; //============================================================================== /** Wakes up any threads that are currently waiting on this object. If signal() is called when nothing is waiting, the next thread to call wait() will return immediately and reset the signal. If the WaitableEvent is manual reset, all current and future threads that wait upon this object will be woken, until reset() is explicitly called. If the WaitableEvent is automatic reset, and one or more threads is waiting upon the object, then one of them will be woken up. If no threads are currently waiting, then the next thread to call wait() will be woken up. As soon as a thread is woken, the signal is automatically reset. @see wait, reset */ void signal() const noexcept; //============================================================================== /** Resets the event to an unsignalled state. If it's not already signalled, this does nothing. */ void reset() const noexcept; private: //============================================================================== #if JUCE_WINDOWS void* handle; #else mutable pthread_cond_t condition; mutable pthread_mutex_t mutex; mutable bool triggered, manualReset; #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaitableEvent) }; #endif // JUCE_WAITABLEEVENT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/time/000077500000000000000000000000001320201440200245005ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp000066400000000000000000000101001320201440200320030ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ static void appendToFile (const File& f, const String& s) { if (f.getFullPathName().isNotEmpty()) { FileOutputStream out (f); if (! out.failedToOpen()) out << s << newLine; } } PerformanceCounter::PerformanceCounter (const String& name, int runsPerPrintout, const File& loggingFile) : runsPerPrint (runsPerPrintout), startTime (0), outputFile (loggingFile) { stats.name = name; appendToFile (outputFile, "**** Counter for \"" + name + "\" started at: " + Time::getCurrentTime().toString (true, true)); } PerformanceCounter::~PerformanceCounter() { printStatistics(); } PerformanceCounter::Statistics::Statistics() noexcept : averageSeconds(), maximumSeconds(), minimumSeconds(), totalSeconds(), numRuns() { } void PerformanceCounter::Statistics::clear() noexcept { averageSeconds = maximumSeconds = minimumSeconds = totalSeconds = 0; numRuns = 0; } void PerformanceCounter::Statistics::addResult (double elapsed) noexcept { if (numRuns == 0) { maximumSeconds = elapsed; minimumSeconds = elapsed; } else { maximumSeconds = jmax (maximumSeconds, elapsed); minimumSeconds = jmin (minimumSeconds, elapsed); } ++numRuns; totalSeconds += elapsed; } static String timeToString (double secs) { return String ((int64) (secs * (secs < 0.01 ? 1000000.0 : 1000.0) + 0.5)) + (secs < 0.01 ? " microsecs" : " millisecs"); } String PerformanceCounter::Statistics::toString() const { MemoryOutputStream s; s << "Performance count for \"" << name << "\" over " << numRuns << " run(s)" << newLine << "Average = " << timeToString (averageSeconds) << ", minimum = " << timeToString (minimumSeconds) << ", maximum = " << timeToString (maximumSeconds) << ", total = " << timeToString (totalSeconds); return s.toString(); } void PerformanceCounter::start() noexcept { startTime = Time::getHighResolutionTicks(); } bool PerformanceCounter::stop() { stats.addResult (Time::highResolutionTicksToSeconds (Time::getHighResolutionTicks() - startTime)); if (stats.numRuns < runsPerPrint) return false; printStatistics(); return true; } void PerformanceCounter::printStatistics() { const String desc (getStatisticsAndReset().toString()); Logger::outputDebugString (desc); appendToFile (outputFile, desc); } PerformanceCounter::Statistics PerformanceCounter::getStatisticsAndReset() { Statistics s (stats); stats.clear(); if (s.numRuns > 0) s.averageSeconds = s.totalSeconds / s.numRuns; return s; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h000066400000000000000000000104651320201440200314660ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_PERFORMANCECOUNTER_H_INCLUDED #define JUCE_PERFORMANCECOUNTER_H_INCLUDED //============================================================================== /** A timer for measuring performance of code and dumping the results to a file. e.g. @code PerformanceCounter pc ("fish", 50, "/temp/myfishlog.txt"); for (;;) { pc.start(); doSomethingFishy(); pc.stop(); } @endcode In this example, the time of each period between calling start/stop will be measured and averaged over 50 runs, and the results printed to a file every 50 times round the loop. */ class JUCE_API PerformanceCounter { public: //============================================================================== /** Creates a PerformanceCounter object. @param counterName the name used when printing out the statistics @param runsPerPrintout the number of start/stop iterations before calling printStatistics() @param loggingFile a file to dump the results to - if this is File::nonexistent, the results are just written to the debugger output */ PerformanceCounter (const String& counterName, int runsPerPrintout = 100, const File& loggingFile = File()); /** Destructor. */ ~PerformanceCounter(); //============================================================================== /** Starts timing. @see stop */ void start() noexcept; /** Stops timing and prints out the results. The number of iterations before doing a printout of the results is set in the constructor. @see start */ bool stop(); /** Dumps the current metrics to the debugger output and to a file. As well as using Logger::outputDebugString to print the results, this will write then to the file specified in the constructor (if this was valid). */ void printStatistics(); /** Holds the current statistics. */ struct Statistics { Statistics() noexcept; void clear() noexcept; String toString() const; void addResult (double elapsed) noexcept; String name; double averageSeconds; double maximumSeconds; double minimumSeconds; double totalSeconds; int64 numRuns; }; /** Returns a copy of the current stats, and resets the internal counter. */ Statistics getStatisticsAndReset(); private: //============================================================================== Statistics stats; int64 runsPerPrint, startTime; File outputFile; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PerformanceCounter) }; #endif // JUCE_PERFORMANCECOUNTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp000066400000000000000000000152601320201440200306100ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ RelativeTime::RelativeTime (const double secs) noexcept : numSeconds (secs) {} RelativeTime::RelativeTime (const RelativeTime& other) noexcept : numSeconds (other.numSeconds) {} RelativeTime::~RelativeTime() noexcept {} //============================================================================== RelativeTime RelativeTime::milliseconds (const int milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } RelativeTime RelativeTime::milliseconds (const int64 milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } RelativeTime RelativeTime::seconds (double s) noexcept { return RelativeTime (s); } RelativeTime RelativeTime::minutes (const double numberOfMinutes) noexcept { return RelativeTime (numberOfMinutes * 60.0); } RelativeTime RelativeTime::hours (const double numberOfHours) noexcept { return RelativeTime (numberOfHours * (60.0 * 60.0)); } RelativeTime RelativeTime::days (const double numberOfDays) noexcept { return RelativeTime (numberOfDays * (60.0 * 60.0 * 24.0)); } RelativeTime RelativeTime::weeks (const double numberOfWeeks) noexcept { return RelativeTime (numberOfWeeks * (60.0 * 60.0 * 24.0 * 7.0)); } //============================================================================== int64 RelativeTime::inMilliseconds() const noexcept { return (int64) (numSeconds * 1000.0); } double RelativeTime::inMinutes() const noexcept { return numSeconds / 60.0; } double RelativeTime::inHours() const noexcept { return numSeconds / (60.0 * 60.0); } double RelativeTime::inDays() const noexcept { return numSeconds / (60.0 * 60.0 * 24.0); } double RelativeTime::inWeeks() const noexcept { return numSeconds / (60.0 * 60.0 * 24.0 * 7.0); } //============================================================================== RelativeTime& RelativeTime::operator= (const RelativeTime& other) noexcept { numSeconds = other.numSeconds; return *this; } RelativeTime RelativeTime::operator+= (RelativeTime t) noexcept { numSeconds += t.numSeconds; return *this; } RelativeTime RelativeTime::operator-= (RelativeTime t) noexcept { numSeconds -= t.numSeconds; return *this; } RelativeTime RelativeTime::operator+= (const double secs) noexcept { numSeconds += secs; return *this; } RelativeTime RelativeTime::operator-= (const double secs) noexcept { numSeconds -= secs; return *this; } RelativeTime operator+ (RelativeTime t1, RelativeTime t2) noexcept { return t1 += t2; } RelativeTime operator- (RelativeTime t1, RelativeTime t2) noexcept { return t1 -= t2; } bool operator== (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() == t2.inSeconds(); } bool operator!= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() != t2.inSeconds(); } bool operator> (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() > t2.inSeconds(); } bool operator< (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() < t2.inSeconds(); } bool operator>= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() >= t2.inSeconds(); } bool operator<= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() <= t2.inSeconds(); } //============================================================================== static void translateTimeField (String& result, int n, const char* singular, const char* plural) { result << TRANS (n == 1 ? singular : plural) .replace (n == 1 ? "1" : "2", String (n)) << ' '; } String RelativeTime::getDescription (const String& returnValueForZeroTime) const { if (numSeconds < 0.001 && numSeconds > -0.001) return returnValueForZeroTime; String result; result.preallocateBytes (32); if (numSeconds < 0) result << '-'; int fieldsShown = 0; int n = std::abs ((int) inWeeks()); if (n > 0) { translateTimeField (result, n, NEEDS_TRANS("1 week"), NEEDS_TRANS("2 weeks")); ++fieldsShown; } n = std::abs ((int) inDays()) % 7; if (n > 0) { translateTimeField (result, n, NEEDS_TRANS("1 day"), NEEDS_TRANS("2 days")); ++fieldsShown; } if (fieldsShown < 2) { n = std::abs ((int) inHours()) % 24; if (n > 0) { translateTimeField (result, n, NEEDS_TRANS("1 hr"), NEEDS_TRANS("2 hrs")); ++fieldsShown; } if (fieldsShown < 2) { n = std::abs ((int) inMinutes()) % 60; if (n > 0) { translateTimeField (result, n, NEEDS_TRANS("1 min"), NEEDS_TRANS("2 mins")); ++fieldsShown; } if (fieldsShown < 2) { n = std::abs ((int) inSeconds()) % 60; if (n > 0) { translateTimeField (result, n, NEEDS_TRANS("1 sec"), NEEDS_TRANS("2 secs")); ++fieldsShown; } if (fieldsShown == 0) { n = std::abs ((int) inMilliseconds()) % 1000; if (n > 0) result << n << ' ' << TRANS ("ms"); } } } } return result.trimEnd(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h000066400000000000000000000166251320201440200302630ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_RELATIVETIME_H_INCLUDED #define JUCE_RELATIVETIME_H_INCLUDED //============================================================================== /** A relative measure of time. The time is stored as a number of seconds, at double-precision floating point accuracy, and may be positive or negative. If you need an absolute time, (i.e. a date + time), see the Time class. */ class JUCE_API RelativeTime { public: //============================================================================== /** Creates a RelativeTime. @param seconds the number of seconds, which may be +ve or -ve. @see milliseconds, minutes, hours, days, weeks */ explicit RelativeTime (double seconds = 0.0) noexcept; /** Copies another relative time. */ RelativeTime (const RelativeTime& other) noexcept; /** Copies another relative time. */ RelativeTime& operator= (const RelativeTime& other) noexcept; /** Destructor. */ ~RelativeTime() noexcept; //============================================================================== /** Creates a new RelativeTime object representing a number of milliseconds. @see seconds, minutes, hours, days, weeks */ static RelativeTime milliseconds (int milliseconds) noexcept; /** Creates a new RelativeTime object representing a number of milliseconds. @see seconds, minutes, hours, days, weeks */ static RelativeTime milliseconds (int64 milliseconds) noexcept; /** Creates a new RelativeTime object representing a number of seconds. @see milliseconds, minutes, hours, days, weeks */ static RelativeTime seconds (double seconds) noexcept; /** Creates a new RelativeTime object representing a number of minutes. @see milliseconds, hours, days, weeks */ static RelativeTime minutes (double numberOfMinutes) noexcept; /** Creates a new RelativeTime object representing a number of hours. @see milliseconds, minutes, days, weeks */ static RelativeTime hours (double numberOfHours) noexcept; /** Creates a new RelativeTime object representing a number of days. @see milliseconds, minutes, hours, weeks */ static RelativeTime days (double numberOfDays) noexcept; /** Creates a new RelativeTime object representing a number of weeks. @see milliseconds, minutes, hours, days */ static RelativeTime weeks (double numberOfWeeks) noexcept; //============================================================================== /** Returns the number of milliseconds this time represents. @see milliseconds, inSeconds, inMinutes, inHours, inDays, inWeeks */ int64 inMilliseconds() const noexcept; /** Returns the number of seconds this time represents. @see inMilliseconds, inMinutes, inHours, inDays, inWeeks */ double inSeconds() const noexcept { return numSeconds; } /** Returns the number of minutes this time represents. @see inMilliseconds, inSeconds, inHours, inDays, inWeeks */ double inMinutes() const noexcept; /** Returns the number of hours this time represents. @see inMilliseconds, inSeconds, inMinutes, inDays, inWeeks */ double inHours() const noexcept; /** Returns the number of days this time represents. @see inMilliseconds, inSeconds, inMinutes, inHours, inWeeks */ double inDays() const noexcept; /** Returns the number of weeks this time represents. @see inMilliseconds, inSeconds, inMinutes, inHours, inDays */ double inWeeks() const noexcept; /** Returns a readable textual description of the time. The exact format of the string returned will depend on the magnitude of the time - e.g. "1 min 4 secs", "1 hr 45 mins", "2 weeks 5 days", "140 ms" so that only the two most significant units are printed. The returnValueForZeroTime value is the result that is returned if the length is zero. Depending on your application you might want to use this to return something more relevant like "empty" or "0 secs", etc. @see inMilliseconds, inSeconds, inMinutes, inHours, inDays, inWeeks */ String getDescription (const String& returnValueForZeroTime = "0") const; //============================================================================== /** Adds another RelativeTime to this one. */ RelativeTime operator+= (RelativeTime timeToAdd) noexcept; /** Subtracts another RelativeTime from this one. */ RelativeTime operator-= (RelativeTime timeToSubtract) noexcept; /** Adds a number of seconds to this time. */ RelativeTime operator+= (double secondsToAdd) noexcept; /** Subtracts a number of seconds from this time. */ RelativeTime operator-= (double secondsToSubtract) noexcept; private: //============================================================================== double numSeconds; }; //============================================================================== /** Compares two RelativeTimes. */ bool operator== (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ bool operator!= (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ bool operator> (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ bool operator< (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ bool operator>= (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ bool operator<= (RelativeTime t1, RelativeTime t2) noexcept; //============================================================================== /** Adds two RelativeTimes together. */ RelativeTime operator+ (RelativeTime t1, RelativeTime t2) noexcept; /** Subtracts two RelativeTimes. */ RelativeTime operator- (RelativeTime t1, RelativeTime t2) noexcept; #endif // JUCE_RELATIVETIME_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp000066400000000000000000000375401320201440200271210ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ namespace TimeHelpers { static struct tm millisToLocal (const int64 millis) noexcept { struct tm result; const int64 seconds = millis / 1000; if (seconds < 86400LL || seconds >= 2145916800LL) { // use extended maths for dates beyond 1970 to 2037.. const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000); const int64 jdm = seconds + timeZoneAdjustment + 210866803200LL; const int days = (int) (jdm / 86400LL); const int a = 32044 + days; const int b = (4 * a + 3) / 146097; const int c = a - (b * 146097) / 4; const int d = (4 * c + 3) / 1461; const int e = c - (d * 1461) / 4; const int m = (5 * e + 2) / 153; result.tm_mday = e - (153 * m + 2) / 5 + 1; result.tm_mon = m + 2 - 12 * (m / 10); result.tm_year = b * 100 + d - 6700 + (m / 10); result.tm_wday = (days + 1) % 7; result.tm_yday = -1; int t = (int) (jdm % 86400LL); result.tm_hour = t / 3600; t %= 3600; result.tm_min = t / 60; result.tm_sec = t % 60; result.tm_isdst = -1; } else { time_t now = static_cast (seconds); #if JUCE_WINDOWS #ifdef _INC_TIME_INL if (now >= 0 && now <= 0x793406fff) localtime_s (&result, &now); else zerostruct (result); #else result = *localtime (&now); #endif #else localtime_r (&now, &result); // more thread-safe #endif } return result; } static int extendedModulo (const int64 value, const int modulo) noexcept { return (int) (value >= 0 ? (value % modulo) : (value - ((value / modulo) + 1) * modulo)); } static inline String formatString (const String& format, const struct tm* const tm) { #if JUCE_ANDROID typedef CharPointer_UTF8 StringType; #elif JUCE_WINDOWS typedef CharPointer_UTF16 StringType; #else typedef CharPointer_UTF32 StringType; #endif for (size_t bufferSize = 256; ; bufferSize += 256) { HeapBlock buffer (bufferSize); #if JUCE_ANDROID const size_t numChars = strftime (buffer, bufferSize - 1, format.toUTF8(), tm); #elif JUCE_WINDOWS const size_t numChars = wcsftime (buffer, bufferSize - 1, format.toWideCharPointer(), tm); #else const size_t numChars = wcsftime (buffer, bufferSize - 1, format.toUTF32(), tm); #endif if (numChars > 0 || format.isEmpty()) return String (StringType (buffer), StringType (buffer) + (int) numChars); } } static uint32 lastMSCounterValue = 0; } //============================================================================== Time::Time() noexcept : millisSinceEpoch (0) { } Time::Time (const Time& other) noexcept : millisSinceEpoch (other.millisSinceEpoch) { } Time::Time (const int64 ms) noexcept : millisSinceEpoch (ms) { } Time::Time (const int year, const int month, const int day, const int hours, const int minutes, const int seconds, const int milliseconds, const bool useLocalTime) noexcept { jassert (year > 100); // year must be a 4-digit version if (year < 1971 || year >= 2038 || ! useLocalTime) { // use extended maths for dates beyond 1970 to 2037.. const int timeZoneAdjustment = useLocalTime ? (31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000)) : 0; const int a = (13 - month) / 12; const int y = year + 4800 - a; const int jd = day + (153 * (month + 12 * a - 2) + 2) / 5 + (y * 365) + (y / 4) - (y / 100) + (y / 400) - 32045; const int64 s = ((int64) jd) * 86400LL - 210866803200LL; millisSinceEpoch = 1000 * (s + (hours * 3600 + minutes * 60 + seconds - timeZoneAdjustment)) + milliseconds; } else { struct tm t; t.tm_year = year - 1900; t.tm_mon = month; t.tm_mday = day; t.tm_hour = hours; t.tm_min = minutes; t.tm_sec = seconds; t.tm_isdst = -1; millisSinceEpoch = 1000 * (int64) mktime (&t); if (millisSinceEpoch < 0) millisSinceEpoch = 0; else millisSinceEpoch += milliseconds; } } Time::~Time() noexcept { } Time& Time::operator= (const Time& other) noexcept { millisSinceEpoch = other.millisSinceEpoch; return *this; } //============================================================================== int64 Time::currentTimeMillis() noexcept { #if JUCE_WINDOWS struct _timeb t; #ifdef _INC_TIME_INL _ftime_s (&t); #else _ftime (&t); #endif return ((int64) t.time) * 1000 + t.millitm; #else struct timeval tv; gettimeofday (&tv, nullptr); return ((int64) tv.tv_sec) * 1000 + tv.tv_usec / 1000; #endif } Time JUCE_CALLTYPE Time::getCurrentTime() noexcept { return Time (currentTimeMillis()); } //============================================================================== uint32 juce_millisecondsSinceStartup() noexcept; uint32 Time::getMillisecondCounter() noexcept { const uint32 now = juce_millisecondsSinceStartup(); if (now < TimeHelpers::lastMSCounterValue) { // in multi-threaded apps this might be called concurrently, so // make sure that our last counter value only increases and doesn't // go backwards.. if (now < TimeHelpers::lastMSCounterValue - 1000) TimeHelpers::lastMSCounterValue = now; } else { TimeHelpers::lastMSCounterValue = now; } return now; } uint32 Time::getApproximateMillisecondCounter() noexcept { if (TimeHelpers::lastMSCounterValue == 0) getMillisecondCounter(); return TimeHelpers::lastMSCounterValue; } void Time::waitForMillisecondCounter (const uint32 targetTime) noexcept { for (;;) { const uint32 now = getMillisecondCounter(); if (now >= targetTime) break; const int toWait = (int) (targetTime - now); if (toWait > 2) { Thread::sleep (jmin (20, toWait >> 1)); } else { // xxx should consider using mutex_pause on the mac as it apparently // makes it seem less like a spinlock and avoids lowering the thread pri. for (int i = 10; --i >= 0;) Thread::yield(); } } } //============================================================================== double Time::highResolutionTicksToSeconds (const int64 ticks) noexcept { return ticks / (double) getHighResolutionTicksPerSecond(); } int64 Time::secondsToHighResolutionTicks (const double seconds) noexcept { return (int64) (seconds * (double) getHighResolutionTicksPerSecond()); } //============================================================================== String Time::toString (const bool includeDate, const bool includeTime, const bool includeSeconds, const bool use24HourClock) const noexcept { String result; if (includeDate) { result << getDayOfMonth() << ' ' << getMonthName (true) << ' ' << getYear(); if (includeTime) result << ' '; } if (includeTime) { const int mins = getMinutes(); result << (use24HourClock ? getHours() : getHoursInAmPmFormat()) << (mins < 10 ? ":0" : ":") << mins; if (includeSeconds) { const int secs = getSeconds(); result << (secs < 10 ? ":0" : ":") << secs; } if (! use24HourClock) result << (isAfternoon() ? "pm" : "am"); } return result.trimEnd(); } String Time::formatted (const String& format) const { struct tm t (TimeHelpers::millisToLocal (millisSinceEpoch)); return TimeHelpers::formatString (format, &t); } //============================================================================== int Time::getYear() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_year + 1900; } int Time::getMonth() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_mon; } int Time::getDayOfYear() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_yday; } int Time::getDayOfMonth() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_mday; } int Time::getDayOfWeek() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_wday; } int Time::getHours() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_hour; } int Time::getMinutes() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_min; } int Time::getSeconds() const noexcept { return TimeHelpers::extendedModulo (millisSinceEpoch / 1000, 60); } int Time::getMilliseconds() const noexcept { return TimeHelpers::extendedModulo (millisSinceEpoch, 1000); } int Time::getHoursInAmPmFormat() const noexcept { const int hours = getHours(); if (hours == 0) return 12; if (hours <= 12) return hours; return hours - 12; } bool Time::isAfternoon() const noexcept { return getHours() >= 12; } bool Time::isDaylightSavingTime() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_isdst != 0; } String Time::getTimeZone() const noexcept { String zone[2]; #if JUCE_MSVC _tzset(); #ifdef _INC_TIME_INL for (int i = 0; i < 2; ++i) { char name[128] = { 0 }; size_t length; _get_tzname (&length, name, 127, i); zone[i] = name; } #else const char** const zonePtr = (const char**) _tzname; zone[0] = zonePtr[0]; zone[1] = zonePtr[1]; #endif #else #if JUCE_MINGW #warning "Can't find a replacement for tzset on mingw - ideas welcome!" #else tzset(); #endif const char** const zonePtr = (const char**) tzname; zone[0] = zonePtr[0]; zone[1] = zonePtr[1]; #endif if (isDaylightSavingTime()) { zone[0] = zone[1]; if (zone[0].length() > 3 && zone[0].containsIgnoreCase ("daylight") && zone[0].contains ("GMT")) zone[0] = "BST"; } return zone[0].substring (0, 3); } String Time::getMonthName (const bool threeLetterVersion) const { return getMonthName (getMonth(), threeLetterVersion); } String Time::getWeekdayName (const bool threeLetterVersion) const { return getWeekdayName (getDayOfWeek(), threeLetterVersion); } static const char* const shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char* const longMonthNames[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; String Time::getMonthName (int monthNumber, const bool threeLetterVersion) { monthNumber %= 12; return TRANS (threeLetterVersion ? shortMonthNames [monthNumber] : longMonthNames [monthNumber]); } String Time::getWeekdayName (int day, const bool threeLetterVersion) { static const char* const shortDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char* const longDayNames[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; day %= 7; return TRANS (threeLetterVersion ? shortDayNames [day] : longDayNames [day]); } //============================================================================== Time& Time::operator+= (RelativeTime delta) noexcept { millisSinceEpoch += delta.inMilliseconds(); return *this; } Time& Time::operator-= (RelativeTime delta) noexcept { millisSinceEpoch -= delta.inMilliseconds(); return *this; } Time operator+ (Time time, RelativeTime delta) noexcept { Time t (time); return t += delta; } Time operator- (Time time, RelativeTime delta) noexcept { Time t (time); return t -= delta; } Time operator+ (RelativeTime delta, Time time) noexcept { Time t (time); return t += delta; } const RelativeTime operator- (Time time1, Time time2) noexcept { return RelativeTime::milliseconds (time1.toMilliseconds() - time2.toMilliseconds()); } bool operator== (Time time1, Time time2) noexcept { return time1.toMilliseconds() == time2.toMilliseconds(); } bool operator!= (Time time1, Time time2) noexcept { return time1.toMilliseconds() != time2.toMilliseconds(); } bool operator< (Time time1, Time time2) noexcept { return time1.toMilliseconds() < time2.toMilliseconds(); } bool operator> (Time time1, Time time2) noexcept { return time1.toMilliseconds() > time2.toMilliseconds(); } bool operator<= (Time time1, Time time2) noexcept { return time1.toMilliseconds() <= time2.toMilliseconds(); } bool operator>= (Time time1, Time time2) noexcept { return time1.toMilliseconds() >= time2.toMilliseconds(); } static int getMonthNumberForCompileDate (const String& m) noexcept { for (int i = 0; i < 12; ++i) if (m.equalsIgnoreCase (shortMonthNames[i])) return i; // If you hit this because your compiler has a non-standard __DATE__ format, // let me know so we can add support for it! jassertfalse; return 0; } Time Time::getCompilationDate() { StringArray dateTokens; dateTokens.addTokens (__DATE__, true); dateTokens.removeEmptyStrings (true); return Time (dateTokens[2].getIntValue(), getMonthNumberForCompileDate (dateTokens[0]), dateTokens[1].getIntValue(), 12, 0); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/time/juce_Time.h000066400000000000000000000405051320201440200265610ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_TIME_H_INCLUDED #define JUCE_TIME_H_INCLUDED //============================================================================== /** Holds an absolute date and time. Internally, the time is stored at millisecond precision. @see RelativeTime */ class JUCE_API Time { public: //============================================================================== /** Creates a Time object. This default constructor creates a time of 1st January 1970, (which is represented internally as 0ms). To create a time object representing the current time, use getCurrentTime(). @see getCurrentTime */ Time() noexcept; /** Creates a time based on a number of milliseconds. The internal millisecond count is set to 0 (1st January 1970). To create a time object set to the current time, use getCurrentTime(). @param millisecondsSinceEpoch the number of milliseconds since the unix 'epoch' (midnight Jan 1st 1970). @see getCurrentTime, currentTimeMillis */ explicit Time (int64 millisecondsSinceEpoch) noexcept; /** Creates a time from a set of date components. The timezone is assumed to be whatever the system is using as its locale. @param year the year, in 4-digit format, e.g. 2004 @param month the month, in the range 0 to 11 @param day the day of the month, in the range 1 to 31 @param hours hours in 24-hour clock format, 0 to 23 @param minutes minutes 0 to 59 @param seconds seconds 0 to 59 @param milliseconds milliseconds 0 to 999 @param useLocalTime if true, encode using the current machine's local time; if false, it will always work in GMT. */ Time (int year, int month, int day, int hours, int minutes, int seconds = 0, int milliseconds = 0, bool useLocalTime = true) noexcept; /** Creates a copy of another Time object. */ Time (const Time& other) noexcept; /** Destructor. */ ~Time() noexcept; /** Copies this time from another one. */ Time& operator= (const Time& other) noexcept; //============================================================================== /** Returns a Time object that is set to the current system time. @see currentTimeMillis */ static Time JUCE_CALLTYPE getCurrentTime() noexcept; /** Returns the time as a number of milliseconds. @returns the number of milliseconds this Time object represents, since midnight jan 1st 1970. @see getMilliseconds */ int64 toMilliseconds() const noexcept { return millisSinceEpoch; } /** Returns the year. A 4-digit format is used, e.g. 2004. */ int getYear() const noexcept; /** Returns the number of the month. The value returned is in the range 0 to 11. @see getMonthName */ int getMonth() const noexcept; /** Returns the name of the month. @param threeLetterVersion if true, it'll be a 3-letter abbreviation, e.g. "Jan"; if false it'll return the long form, e.g. "January" @see getMonth */ String getMonthName (bool threeLetterVersion) const; /** Returns the day of the month. The value returned is in the range 1 to 31. */ int getDayOfMonth() const noexcept; /** Returns the number of the day of the week. The value returned is in the range 0 to 6 (0 = sunday, 1 = monday, etc). */ int getDayOfWeek() const noexcept; /** Returns the number of the day of the year. The value returned is in the range 0 to 365. */ int getDayOfYear() const noexcept; /** Returns the name of the weekday. @param threeLetterVersion if true, it'll return a 3-letter abbreviation, e.g. "Tue"; if false, it'll return the full version, e.g. "Tuesday". */ String getWeekdayName (bool threeLetterVersion) const; /** Returns the number of hours since midnight. This is in 24-hour clock format, in the range 0 to 23. @see getHoursInAmPmFormat, isAfternoon */ int getHours() const noexcept; /** Returns true if the time is in the afternoon. So it returns true for "PM", false for "AM". @see getHoursInAmPmFormat, getHours */ bool isAfternoon() const noexcept; /** Returns the hours in 12-hour clock format. This will return a value 1 to 12 - use isAfternoon() to find out whether this is in the afternoon or morning. @see getHours, isAfternoon */ int getHoursInAmPmFormat() const noexcept; /** Returns the number of minutes, 0 to 59. */ int getMinutes() const noexcept; /** Returns the number of seconds, 0 to 59. */ int getSeconds() const noexcept; /** Returns the number of milliseconds, 0 to 999. Unlike toMilliseconds(), this just returns the position within the current second rather than the total number since the epoch. @see toMilliseconds */ int getMilliseconds() const noexcept; /** Returns true if the local timezone uses a daylight saving correction. */ bool isDaylightSavingTime() const noexcept; /** Returns a 3-character string to indicate the local timezone. */ String getTimeZone() const noexcept; //============================================================================== /** Quick way of getting a string version of a date and time. For a more powerful way of formatting the date and time, see the formatted() method. @param includeDate whether to include the date in the string @param includeTime whether to include the time in the string @param includeSeconds if the time is being included, this provides an option not to include the seconds in it @param use24HourClock if the time is being included, sets whether to use am/pm or 24 hour notation. @see formatted */ String toString (bool includeDate, bool includeTime, bool includeSeconds = true, bool use24HourClock = false) const noexcept; /** Converts this date/time to a string with a user-defined format. This uses the C strftime() function to format this time as a string. To save you looking it up, these are the escape codes that strftime uses (other codes might work on some platforms and not others, but these are the common ones): %a is replaced by the locale's abbreviated weekday name. %A is replaced by the locale's full weekday name. %b is replaced by the locale's abbreviated month name. %B is replaced by the locale's full month name. %c is replaced by the locale's appropriate date and time representation. %d is replaced by the day of the month as a decimal number [01,31]. %H is replaced by the hour (24-hour clock) as a decimal number [00,23]. %I is replaced by the hour (12-hour clock) as a decimal number [01,12]. %j is replaced by the day of the year as a decimal number [001,366]. %m is replaced by the month as a decimal number [01,12]. %M is replaced by the minute as a decimal number [00,59]. %p is replaced by the locale's equivalent of either a.m. or p.m. %S is replaced by the second as a decimal number [00,61]. %U is replaced by the week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. %w is replaced by the weekday as a decimal number [0,6], with 0 representing Sunday. %W is replaced by the week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. %x is replaced by the locale's appropriate date representation. %X is replaced by the locale's appropriate time representation. %y is replaced by the year without century as a decimal number [00,99]. %Y is replaced by the year with century as a decimal number. %Z is replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. %% is replaced by %. @see toString */ String formatted (const String& format) const; //============================================================================== /** Adds a RelativeTime to this time. */ Time& operator+= (RelativeTime delta) noexcept; /** Subtracts a RelativeTime from this time. */ Time& operator-= (RelativeTime delta) noexcept; //============================================================================== /** Tries to set the computer's clock. @returns true if this succeeds, although depending on the system, the application might not have sufficient privileges to do this. */ bool setSystemTimeToThisTime() const; //============================================================================== /** Returns the name of a day of the week. @param dayNumber the day, 0 to 6 (0 = sunday, 1 = monday, etc) @param threeLetterVersion if true, it'll return a 3-letter abbreviation, e.g. "Tue"; if false, it'll return the full version, e.g. "Tuesday". */ static String getWeekdayName (int dayNumber, bool threeLetterVersion); /** Returns the name of one of the months. @param monthNumber the month, 0 to 11 @param threeLetterVersion if true, it'll be a 3-letter abbreviation, e.g. "Jan"; if false it'll return the long form, e.g. "January" */ static String getMonthName (int monthNumber, bool threeLetterVersion); //============================================================================== // Static methods for getting system timers directly.. /** Returns the current system time. Returns the number of milliseconds since midnight jan 1st 1970. Should be accurate to within a few millisecs, depending on platform, hardware, etc. */ static int64 currentTimeMillis() noexcept; /** Returns the number of millisecs since a fixed event (usually system startup). This returns a monotonically increasing value which it unaffected by changes to the system clock. It should be accurate to within a few millisecs, depending on platform, hardware, etc. Being a 32-bit return value, it will of course wrap back to 0 after 2^32 seconds of uptime, so be careful to take that into account. If you need a 64-bit time, you can use currentTimeMillis() instead. @see getApproximateMillisecondCounter */ static uint32 getMillisecondCounter() noexcept; /** Returns the number of millisecs since a fixed event (usually system startup). This has the same function as getMillisecondCounter(), but returns a more accurate value, using a higher-resolution timer if one is available. @see getMillisecondCounter */ static double getMillisecondCounterHiRes() noexcept; /** Waits until the getMillisecondCounter() reaches a given value. This will make the thread sleep as efficiently as it can while it's waiting. */ static void waitForMillisecondCounter (uint32 targetTime) noexcept; /** Less-accurate but faster version of getMillisecondCounter(). This will return the last value that getMillisecondCounter() returned, so doesn't need to make a system call, but is less accurate - it shouldn't be more than 100ms away from the correct time, though, so is still accurate enough for a lot of purposes. @see getMillisecondCounter */ static uint32 getApproximateMillisecondCounter() noexcept; //============================================================================== // High-resolution timers.. /** Returns the current high-resolution counter's tick-count. This is a similar idea to getMillisecondCounter(), but with a higher resolution. @see getHighResolutionTicksPerSecond, highResolutionTicksToSeconds, secondsToHighResolutionTicks */ static int64 getHighResolutionTicks() noexcept; /** Returns the resolution of the high-resolution counter in ticks per second. @see getHighResolutionTicks, highResolutionTicksToSeconds, secondsToHighResolutionTicks */ static int64 getHighResolutionTicksPerSecond() noexcept; /** Converts a number of high-resolution ticks into seconds. @see getHighResolutionTicks, getHighResolutionTicksPerSecond, secondsToHighResolutionTicks */ static double highResolutionTicksToSeconds (int64 ticks) noexcept; /** Converts a number seconds into high-resolution ticks. @see getHighResolutionTicks, getHighResolutionTicksPerSecond, highResolutionTicksToSeconds */ static int64 secondsToHighResolutionTicks (double seconds) noexcept; /** Returns a Time based on the value of the __DATE__ macro when this module was compiled */ static Time getCompilationDate(); private: //============================================================================== int64 millisSinceEpoch; }; //============================================================================== /** Adds a RelativeTime to a Time. */ JUCE_API Time operator+ (Time time, RelativeTime delta) noexcept; /** Adds a RelativeTime to a Time. */ JUCE_API Time operator+ (RelativeTime delta, Time time) noexcept; /** Subtracts a RelativeTime from a Time. */ JUCE_API Time operator- (Time time, RelativeTime delta) noexcept; /** Returns the relative time difference between two times. */ JUCE_API const RelativeTime operator- (Time time1, Time time2) noexcept; /** Compares two Time objects. */ JUCE_API bool operator== (Time time1, Time time2) noexcept; /** Compares two Time objects. */ JUCE_API bool operator!= (Time time1, Time time2) noexcept; /** Compares two Time objects. */ JUCE_API bool operator< (Time time1, Time time2) noexcept; /** Compares two Time objects. */ JUCE_API bool operator<= (Time time1, Time time2) noexcept; /** Compares two Time objects. */ JUCE_API bool operator> (Time time1, Time time2) noexcept; /** Compares two Time objects. */ JUCE_API bool operator>= (Time time1, Time time2) noexcept; #endif // JUCE_TIME_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/unit_tests/000077500000000000000000000000001320201440200257435ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp000066400000000000000000000151431320201440200312400ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ UnitTest::UnitTest (const String& nm) : name (nm), runner (nullptr) { getAllTests().add (this); } UnitTest::~UnitTest() { getAllTests().removeFirstMatchingValue (this); } Array& UnitTest::getAllTests() { static Array tests; return tests; } void UnitTest::initialise() {} void UnitTest::shutdown() {} void UnitTest::performTest (UnitTestRunner* const newRunner) { jassert (newRunner != nullptr); runner = newRunner; initialise(); runTest(); shutdown(); } void UnitTest::logMessage (const String& message) { // This method's only valid while the test is being run! jassert (runner != nullptr); runner->logMessage (message); } void UnitTest::beginTest (const String& testName) { // This method's only valid while the test is being run! jassert (runner != nullptr); runner->beginNewTest (this, testName); } void UnitTest::expect (const bool result, const String& failureMessage) { // This method's only valid while the test is being run! jassert (runner != nullptr); if (result) runner->addPass(); else runner->addFail (failureMessage); } Random UnitTest::getRandom() const { // This method's only valid while the test is being run! jassert (runner != nullptr); return runner->randomForTest; } //============================================================================== UnitTestRunner::UnitTestRunner() : currentTest (nullptr), assertOnFailure (true), logPasses (false) { } UnitTestRunner::~UnitTestRunner() { } void UnitTestRunner::setAssertOnFailure (bool shouldAssert) noexcept { assertOnFailure = shouldAssert; } void UnitTestRunner::setPassesAreLogged (bool shouldDisplayPasses) noexcept { logPasses = shouldDisplayPasses; } int UnitTestRunner::getNumResults() const noexcept { return results.size(); } const UnitTestRunner::TestResult* UnitTestRunner::getResult (int index) const noexcept { return results [index]; } void UnitTestRunner::resultsUpdated() { } void UnitTestRunner::runTests (const Array& tests, int64 randomSeed) { results.clear(); resultsUpdated(); if (randomSeed == 0) randomSeed = Random().nextInt (0x7ffffff); randomForTest = Random (randomSeed); logMessage ("Random seed: 0x" + String::toHexString (randomSeed)); for (int i = 0; i < tests.size(); ++i) { if (shouldAbortTests()) break; try { tests.getUnchecked(i)->performTest (this); } catch (...) { addFail ("An unhandled exception was thrown!"); } } endTest(); } void UnitTestRunner::runAllTests (int64 randomSeed) { runTests (UnitTest::getAllTests(), randomSeed); } void UnitTestRunner::logMessage (const String& message) { Logger::writeToLog (message); } bool UnitTestRunner::shouldAbortTests() { return false; } void UnitTestRunner::beginNewTest (UnitTest* const test, const String& subCategory) { endTest(); currentTest = test; TestResult* const r = new TestResult(); results.add (r); r->unitTestName = test->getName(); r->subcategoryName = subCategory; r->passes = 0; r->failures = 0; logMessage ("-----------------------------------------------------------------"); logMessage ("Starting test: " + r->unitTestName + " / " + subCategory + "..."); resultsUpdated(); } void UnitTestRunner::endTest() { if (results.size() > 0) { TestResult* const r = results.getLast(); if (r->failures > 0) { String m ("FAILED!! "); m << r->failures << (r->failures == 1 ? " test" : " tests") << " failed, out of a total of " << (r->passes + r->failures); logMessage (String::empty); logMessage (m); logMessage (String::empty); } else { logMessage ("All tests completed successfully"); } } } void UnitTestRunner::addPass() { { const ScopedLock sl (results.getLock()); TestResult* const r = results.getLast(); jassert (r != nullptr); // You need to call UnitTest::beginTest() before performing any tests! r->passes++; if (logPasses) { String message ("Test "); message << (r->failures + r->passes) << " passed"; logMessage (message); } } resultsUpdated(); } void UnitTestRunner::addFail (const String& failureMessage) { { const ScopedLock sl (results.getLock()); TestResult* const r = results.getLast(); jassert (r != nullptr); // You need to call UnitTest::beginTest() before performing any tests! r->failures++; String message ("!!! Test "); message << (r->failures + r->passes) << " failed"; if (failureMessage.isNotEmpty()) message << ": " << failureMessage; r->messages.add (message); logMessage (message); } resultsUpdated(); if (assertOnFailure) { jassertfalse; } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h000066400000000000000000000263661320201440200307160ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_UNITTEST_H_INCLUDED #define JUCE_UNITTEST_H_INCLUDED class UnitTestRunner; //============================================================================== /** This is a base class for classes that perform a unit test. To write a test using this class, your code should look something like this: @code class MyTest : public UnitTest { public: MyTest() : UnitTest ("Foobar testing") {} void runTest() { beginTest ("Part 1"); expect (myFoobar.doesSomething()); expect (myFoobar.doesSomethingElse()); beginTest ("Part 2"); expect (myOtherFoobar.doesSomething()); expect (myOtherFoobar.doesSomethingElse()); ...etc.. } }; // Creating a static instance will automatically add the instance to the array // returned by UnitTest::getAllTests(), so the test will be included when you call // UnitTestRunner::runAllTests() static MyTest test; @endcode To run a test, use the UnitTestRunner class. @see UnitTestRunner */ class JUCE_API UnitTest { public: //============================================================================== /** Creates a test with the given name. */ explicit UnitTest (const String& name); /** Destructor. */ virtual ~UnitTest(); /** Returns the name of the test. */ const String& getName() const noexcept { return name; } /** Runs the test, using the specified UnitTestRunner. You shouldn't need to call this method directly - use UnitTestRunner::runTests() instead. */ void performTest (UnitTestRunner* runner); /** Returns the set of all UnitTest objects that currently exist. */ static Array& getAllTests(); //============================================================================== /** You can optionally implement this method to set up your test. This method will be called before runTest(). */ virtual void initialise(); /** You can optionally implement this method to clear up after your test has been run. This method will be called after runTest() has returned. */ virtual void shutdown(); /** Implement this method in your subclass to actually run your tests. The content of your implementation should call beginTest() and expect() to perform the tests. */ virtual void runTest() = 0; //============================================================================== /** Tells the system that a new subsection of tests is beginning. This should be called from your runTest() method, and may be called as many times as you like, to demarcate different sets of tests. */ void beginTest (const String& testName); //============================================================================== /** Checks that the result of a test is true, and logs this result. In your runTest() method, you should call this method for each condition that you want to check, e.g. @code void runTest() { beginTest ("basic tests"); expect (x + y == 2); expect (getThing() == someThing); ...etc... } @endcode If testResult is true, a pass is logged; if it's false, a failure is logged. If the failure message is specified, it will be written to the log if the test fails. */ void expect (bool testResult, const String& failureMessage = String::empty); /** Compares two values, and if they don't match, prints out a message containing the expected and actual result values. */ template void expectEquals (ValueType actual, ValueType expected, String failureMessage = String::empty) { const bool result = (actual == expected); if (! result) { if (failureMessage.isNotEmpty()) failureMessage << " -- "; failureMessage << "Expected value: " << expected << ", Actual value: " << actual; } expect (result, failureMessage); } //============================================================================== /** Writes a message to the test log. This can only be called from within your runTest() method. */ void logMessage (const String& message); /** Returns a shared RNG that all unit tests should use. If a test needs random numbers, it's important that when an error is found, the exact circumstances can be re-created in order to re-test the problem, by repeating the test with the same random seed value. To make this possible, the UnitTestRunner class creates a master seed value for the run, writes this number to the log, and then this method returns a Random object based on that seed. All tests should only use this method to create any Random objects that they need. Note that this method will return an identical object each time it's called for a given run, so if you need several different Random objects, the best way to do that is to call Random::combineSeed() on the result to permute it with a constant value. */ Random getRandom() const; private: //============================================================================== const String name; UnitTestRunner* runner; JUCE_DECLARE_NON_COPYABLE (UnitTest) }; //============================================================================== /** Runs a set of unit tests. You can instantiate one of these objects and use it to invoke tests on a set of UnitTest objects. By using a subclass of UnitTestRunner, you can intercept logging messages and perform custom behaviour when each test completes. @see UnitTest */ class JUCE_API UnitTestRunner { public: //============================================================================== /** */ UnitTestRunner(); /** Destructor. */ virtual ~UnitTestRunner(); /** Runs a set of tests. The tests are performed in order, and the results are logged. To run all the registered UnitTest objects that exist, use runAllTests(). If you want to run the tests with a predetermined seed, you can pass that into the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. */ void runTests (const Array& tests, int64 randomSeed = 0); /** Runs all the UnitTest objects that currently exist. This calls runTests() for all the objects listed in UnitTest::getAllTests(). If you want to run the tests with a predetermined seed, you can pass that into the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. */ void runAllTests (int64 randomSeed = 0); /** Sets a flag to indicate whether an assertion should be triggered if a test fails. This is true by default. */ void setAssertOnFailure (bool shouldAssert) noexcept; /** Sets a flag to indicate whether successful tests should be logged. By default, this is set to false, so that only failures will be displayed in the log. */ void setPassesAreLogged (bool shouldDisplayPasses) noexcept; //============================================================================== /** Contains the results of a test. One of these objects is instantiated each time UnitTest::beginTest() is called, and it contains details of the number of subsequent UnitTest::expect() calls that are made. */ struct TestResult { /** The main name of this test (i.e. the name of the UnitTest object being run). */ String unitTestName; /** The name of the current subcategory (i.e. the name that was set when UnitTest::beginTest() was called). */ String subcategoryName; /** The number of UnitTest::expect() calls that succeeded. */ int passes; /** The number of UnitTest::expect() calls that failed. */ int failures; /** A list of messages describing the failed tests. */ StringArray messages; }; /** Returns the number of TestResult objects that have been performed. @see getResult */ int getNumResults() const noexcept; /** Returns one of the TestResult objects that describes a test that has been run. @see getNumResults */ const TestResult* getResult (int index) const noexcept; protected: /** Called when the list of results changes. You can override this to perform some sort of behaviour when results are added. */ virtual void resultsUpdated(); /** Logs a message about the current test progress. By default this just writes the message to the Logger class, but you could override this to do something else with the data. */ virtual void logMessage (const String& message); /** This can be overridden to let the runner know that it should abort the tests as soon as possible, e.g. because the thread needs to stop. */ virtual bool shouldAbortTests(); private: //============================================================================== friend class UnitTest; UnitTest* currentTest; String currentSubCategory; OwnedArray results; bool assertOnFailure, logPasses; Random randomForTest; void beginNewTest (UnitTest* test, const String& subCategory); void endTest(); void addPass(); void addFail (const String& failureMessage); JUCE_DECLARE_NON_COPYABLE (UnitTestRunner) }; #endif // JUCE_UNITTEST_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/xml/000077500000000000000000000000001320201440200243425ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp000066400000000000000000000636651320201440200303330ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ XmlDocument::XmlDocument (const String& documentText) : originalText (documentText), input (nullptr), outOfData (false), errorOccurred (false), needToLoadDTD (false), ignoreEmptyTextElements (true) { } XmlDocument::XmlDocument (const File& file) : input (nullptr), outOfData (false), errorOccurred (false), needToLoadDTD (false), ignoreEmptyTextElements (true), inputSource (new FileInputSource (file)) { } XmlDocument::~XmlDocument() { } XmlElement* XmlDocument::parse (const File& file) { XmlDocument doc (file); return doc.getDocumentElement(); } XmlElement* XmlDocument::parse (const String& xmlData) { XmlDocument doc (xmlData); return doc.getDocumentElement(); } void XmlDocument::setInputSource (InputSource* const newSource) noexcept { inputSource = newSource; } void XmlDocument::setEmptyTextElementsIgnored (const bool shouldBeIgnored) noexcept { ignoreEmptyTextElements = shouldBeIgnored; } namespace XmlIdentifierChars { static bool isIdentifierCharSlow (const juce_wchar c) noexcept { return CharacterFunctions::isLetterOrDigit (c) || c == '_' || c == '-' || c == ':' || c == '.'; } static bool isIdentifierChar (const juce_wchar c) noexcept { static const uint32 legalChars[] = { 0, 0x7ff6000, 0x87fffffe, 0x7fffffe, 0 }; return ((int) c < (int) numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (1 << (c & 31))) != 0) : isIdentifierCharSlow (c); } /*static void generateIdentifierCharConstants() { uint32 n[8] = { 0 }; for (int i = 0; i < 256; ++i) if (isIdentifierCharSlow (i)) n[i >> 5] |= (1 << (i & 31)); String s; for (int i = 0; i < 8; ++i) s << "0x" << String::toHexString ((int) n[i]) << ", "; DBG (s); }*/ static String::CharPointerType findEndOfToken (String::CharPointerType p) { while (isIdentifierChar (*p)) ++p; return p; } } XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) { if (originalText.isEmpty() && inputSource != nullptr) { ScopedPointer in (inputSource->createInputStream()); if (in != nullptr) { MemoryOutputStream data; data.writeFromInputStream (*in, onlyReadOuterDocumentElement ? 8192 : -1); #if JUCE_STRING_UTF_TYPE == 8 if (data.getDataSize() > 2) { data.writeByte (0); const char* text = static_cast (data.getData()); if (CharPointer_UTF16::isByteOrderMarkBigEndian (text) || CharPointer_UTF16::isByteOrderMarkLittleEndian (text)) { originalText = data.toString(); } else { if (CharPointer_UTF8::isByteOrderMark (text)) text += 3; // parse the input buffer directly to avoid copying it all to a string.. return parseDocumentElement (String::CharPointerType (text), onlyReadOuterDocumentElement); } } #else originalText = data.toString(); #endif } } return parseDocumentElement (originalText.getCharPointer(), onlyReadOuterDocumentElement); } const String& XmlDocument::getLastParseError() const noexcept { return lastError; } void XmlDocument::setLastError (const String& desc, const bool carryOn) { lastError = desc; errorOccurred = ! carryOn; } String XmlDocument::getFileContents (const String& filename) const { if (inputSource != nullptr) { const ScopedPointer in (inputSource->createInputStreamFor (filename.trim().unquoted())); if (in != nullptr) return in->readEntireStreamAsString(); } return String::empty; } juce_wchar XmlDocument::readNextChar() noexcept { const juce_wchar c = input.getAndAdvance(); if (c == 0) { outOfData = true; --input; } return c; } XmlElement* XmlDocument::parseDocumentElement (String::CharPointerType textToParse, const bool onlyReadOuterDocumentElement) { input = textToParse; errorOccurred = false; outOfData = false; needToLoadDTD = true; if (textToParse.isEmpty()) { lastError = "not enough input"; } else if (! parseHeader()) { lastError = "malformed header"; } else if (! parseDTD()) { lastError = "malformed DTD"; } else { lastError.clear(); ScopedPointer result (readNextElement (! onlyReadOuterDocumentElement)); if (! errorOccurred) return result.release(); } return nullptr; } bool XmlDocument::parseHeader() { skipNextWhiteSpace(); if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII (""))); if (headerEnd.isEmpty()) return false; #if JUCE_DEBUG const String encoding (String (input, headerEnd) .fromFirstOccurrenceOf ("encoding", false, true) .fromFirstOccurrenceOf ("=", false, false) .fromFirstOccurrenceOf ("\"", false, false) .upToFirstOccurrenceOf ("\"", false, false).trim()); /* If you load an XML document with a non-UTF encoding type, it may have been loaded wrongly.. Since all the files are read via the normal juce file streams, they're treated as UTF-8, so by the time it gets to the parser, the encoding will have been lost. Best plan is to stick to utf-8 or if you have specific files to read, use your own code to convert them to a unicode String, and pass that to the XML parser. */ jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-")); #endif input = headerEnd + 2; skipNextWhiteSpace(); } return true; } bool XmlDocument::parseDTD() { if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII (" 0;) { const juce_wchar c = readNextChar(); if (outOfData) return false; if (c == '<') ++n; else if (c == '>') --n; } dtdText = String (dtdStart, input - 1).trim(); } return true; } void XmlDocument::skipNextWhiteSpace() { for (;;) { input = input.findEndOfWhitespace(); if (input.isEmpty()) { outOfData = true; break; } if (*input == '<') { if (input[1] == '!' && input[2] == '-' && input[3] == '-') { input += 4; const int closeComment = input.indexOf (CharPointer_ASCII ("-->")); if (closeComment < 0) { outOfData = true; break; } input += closeComment + 3; continue; } if (input[1] == '?') { input += 2; const int closeBracket = input.indexOf (CharPointer_ASCII ("?>")); if (closeBracket < 0) { outOfData = true; break; } input += closeBracket + 2; continue; } } break; } } void XmlDocument::readQuotedString (String& result) { const juce_wchar quote = readNextChar(); while (! outOfData) { const juce_wchar c = readNextChar(); if (c == quote) break; --input; if (c == '&') { readEntity (result); } else { const String::CharPointerType start (input); for (;;) { const juce_wchar character = *input; if (character == quote) { result.appendCharPointer (start, input); ++input; return; } else if (character == '&') { result.appendCharPointer (start, input); break; } else if (character == 0) { setLastError ("unmatched quotes", false); outOfData = true; break; } ++input; } } } } XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) { XmlElement* node = nullptr; skipNextWhiteSpace(); if (outOfData) return nullptr; if (*input == '<') { ++input; String::CharPointerType endOfToken (XmlIdentifierChars::findEndOfToken (input)); if (endOfToken == input) { // no tag name - but allow for a gap after the '<' before giving an error skipNextWhiteSpace(); endOfToken = XmlIdentifierChars::findEndOfToken (input); if (endOfToken == input) { setLastError ("tag name missing", false); return node; } } node = new XmlElement (input, endOfToken); input = endOfToken; LinkedListPointer::Appender attributeAppender (node->attributes); // look for attributes for (;;) { skipNextWhiteSpace(); const juce_wchar c = *input; // empty tag.. if (c == '/' && input[1] == '>') { input += 2; break; } // parse the guts of the element.. if (c == '>') { ++input; if (alsoParseSubElements) readChildElements (*node); break; } // get an attribute.. if (XmlIdentifierChars::isIdentifierChar (c)) { String::CharPointerType attNameEnd (XmlIdentifierChars::findEndOfToken (input)); if (attNameEnd != input) { const String::CharPointerType attNameStart (input); input = attNameEnd; skipNextWhiteSpace(); if (readNextChar() == '=') { skipNextWhiteSpace(); const juce_wchar nextChar = *input; if (nextChar == '"' || nextChar == '\'') { XmlElement::XmlAttributeNode* const newAtt = new XmlElement::XmlAttributeNode (attNameStart, attNameEnd); readQuotedString (newAtt->value); attributeAppender.append (newAtt); continue; } } else { setLastError ("expected '=' after attribute '" + String (attNameStart, attNameEnd) + "'", false); return node; } } } else { if (! outOfData) setLastError ("illegal character found in " + node->getTagName() + ": '" + c + "'", false); } break; } } return node; } void XmlDocument::readChildElements (XmlElement& parent) { LinkedListPointer::Appender childAppender (parent.firstChildElement); for (;;) { const String::CharPointerType preWhitespaceInput (input); skipNextWhiteSpace(); if (outOfData) { setLastError ("unmatched tags", false); break; } if (*input == '<') { const juce_wchar c1 = input[1]; if (c1 == '/') { // our close tag.. const int closeTag = input.indexOf ((juce_wchar) '>'); if (closeTag >= 0) input += closeTag + 1; break; } if (c1 == '!' && CharacterFunctions::compareUpTo (input + 2, CharPointer_ASCII ("[CDATA["), 7) == 0) { input += 9; const String::CharPointerType inputStart (input); for (;;) { const juce_wchar c0 = *input; if (c0 == 0) { setLastError ("unterminated CDATA section", false); outOfData = true; break; } else if (c0 == ']' && input[1] == ']' && input[2] == '>') { childAppender.append (XmlElement::createTextElement (String (inputStart, input))); input += 3; break; } ++input; } } else { // this is some other element, so parse and add it.. if (XmlElement* const n = readNextElement (true)) childAppender.append (n); else break; } } else // must be a character block { input = preWhitespaceInput; // roll back to include the leading whitespace MemoryOutputStream textElementContent; bool contentShouldBeUsed = ! ignoreEmptyTextElements; for (;;) { const juce_wchar c = *input; if (c == '<') { if (input[1] == '!' && input[2] == '-' && input[3] == '-') { input += 4; const int closeComment = input.indexOf (CharPointer_ASCII ("-->")); if (closeComment < 0) { setLastError ("unterminated comment", false); outOfData = true; return; } input += closeComment + 3; continue; } break; } if (c == 0) { setLastError ("unmatched tags", false); outOfData = true; return; } if (c == '&') { String entity; readEntity (entity); if (entity.startsWithChar ('<') && entity [1] != 0) { const String::CharPointerType oldInput (input); const bool oldOutOfData = outOfData; input = entity.getCharPointer(); outOfData = false; while (XmlElement* n = readNextElement (true)) childAppender.append (n); input = oldInput; outOfData = oldOutOfData; } else { textElementContent << entity; contentShouldBeUsed = contentShouldBeUsed || entity.containsNonWhitespaceChars(); } } else { for (;;) { const juce_wchar nextChar = *input; if (nextChar == '<' || nextChar == '&') break; if (nextChar == 0) { setLastError ("unmatched tags", false); outOfData = true; return; } textElementContent.appendUTF8Char (nextChar); contentShouldBeUsed = contentShouldBeUsed || ! CharacterFunctions::isWhitespace (nextChar); ++input; } } } if (contentShouldBeUsed) childAppender.append (XmlElement::createTextElement (textElementContent.toUTF8())); } } } void XmlDocument::readEntity (String& result) { // skip over the ampersand ++input; if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("amp;"), 4) == 0) { input += 4; result += '&'; } else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("quot;"), 5) == 0) { input += 5; result += '"'; } else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("apos;"), 5) == 0) { input += 5; result += '\''; } else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("lt;"), 3) == 0) { input += 3; result += '<'; } else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("gt;"), 3) == 0) { input += 3; result += '>'; } else if (*input == '#') { int charCode = 0; ++input; if (*input == 'x' || *input == 'X') { ++input; int numChars = 0; while (input[0] != ';') { const int hexValue = CharacterFunctions::getHexDigitValue (input[0]); if (hexValue < 0 || ++numChars > 8) { setLastError ("illegal escape sequence", true); break; } charCode = (charCode << 4) | hexValue; ++input; } ++input; } else if (input[0] >= '0' && input[0] <= '9') { int numChars = 0; while (input[0] != ';') { if (++numChars > 12) { setLastError ("illegal escape sequence", true); break; } charCode = charCode * 10 + ((int) input[0] - '0'); ++input; } ++input; } else { setLastError ("illegal escape sequence", true); result += '&'; return; } result << (juce_wchar) charCode; } else { const String::CharPointerType entityNameStart (input); const int closingSemiColon = input.indexOf ((juce_wchar) ';'); if (closingSemiColon < 0) { outOfData = true; result += '&'; } else { input += closingSemiColon + 1; result += expandExternalEntity (String (entityNameStart, (size_t) closingSemiColon)); } } } String XmlDocument::expandEntity (const String& ent) { if (ent.equalsIgnoreCase ("amp")) return String::charToString ('&'); if (ent.equalsIgnoreCase ("quot")) return String::charToString ('"'); if (ent.equalsIgnoreCase ("apos")) return String::charToString ('\''); if (ent.equalsIgnoreCase ("lt")) return String::charToString ('<'); if (ent.equalsIgnoreCase ("gt")) return String::charToString ('>'); if (ent[0] == '#') { const juce_wchar char1 = ent[1]; if (char1 == 'x' || char1 == 'X') return String::charToString (static_cast (ent.substring (2).getHexValue32())); if (char1 >= '0' && char1 <= '9') return String::charToString (static_cast (ent.substring (1).getIntValue())); setLastError ("illegal escape sequence", false); return String::charToString ('&'); } return expandExternalEntity (ent); } String XmlDocument::expandExternalEntity (const String& entity) { if (needToLoadDTD) { if (dtdText.isNotEmpty()) { dtdText = dtdText.trimCharactersAtEnd (">"); tokenisedDTD.addTokens (dtdText, true); if (tokenisedDTD [tokenisedDTD.size() - 2].equalsIgnoreCase ("system") && tokenisedDTD [tokenisedDTD.size() - 1].isQuotedString()) { const String fn (tokenisedDTD [tokenisedDTD.size() - 1]); tokenisedDTD.clear(); tokenisedDTD.addTokens (getFileContents (fn), true); } else { tokenisedDTD.clear(); const int openBracket = dtdText.indexOfChar ('['); if (openBracket > 0) { const int closeBracket = dtdText.lastIndexOfChar (']'); if (closeBracket > openBracket) tokenisedDTD.addTokens (dtdText.substring (openBracket + 1, closeBracket), true); } } for (int i = tokenisedDTD.size(); --i >= 0;) { if (tokenisedDTD[i].startsWithChar ('%') && tokenisedDTD[i].endsWithChar (';')) { const String parsed (getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1))); StringArray newToks; newToks.addTokens (parsed, true); tokenisedDTD.remove (i); for (int j = newToks.size(); --j >= 0;) tokenisedDTD.insert (i, newToks[j]); } } } needToLoadDTD = false; } for (int i = 0; i < tokenisedDTD.size(); ++i) { if (tokenisedDTD[i] == entity) { if (tokenisedDTD[i - 1].equalsIgnoreCase ("").trim().unquoted()); // check for sub-entities.. int ampersand = ent.indexOfChar ('&'); while (ampersand >= 0) { const int semiColon = ent.indexOf (i + 1, ";"); if (semiColon < 0) { setLastError ("entity without terminating semi-colon", false); break; } const String resolved (expandEntity (ent.substring (i + 1, semiColon))); ent = ent.substring (0, ampersand) + resolved + ent.substring (semiColon + 1); ampersand = ent.indexOfChar (semiColon + 1, '&'); } return ent; } } } setLastError ("unknown entity", true); return entity; } String XmlDocument::getParameterEntity (const String& entity) { for (int i = 0; i < tokenisedDTD.size(); ++i) { if (tokenisedDTD[i] == entity && tokenisedDTD [i - 1] == "%" && tokenisedDTD [i - 2].equalsIgnoreCase ("")); if (ent.equalsIgnoreCase ("system")) return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">")); return ent.trim().unquoted(); } } return entity; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h000066400000000000000000000162121320201440200277620ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_XMLDOCUMENT_H_INCLUDED #define JUCE_XMLDOCUMENT_H_INCLUDED //============================================================================== /** Parses a text-based XML document and creates an XmlElement object from it. The parser will parse DTDs to load external entities but won't check the document for validity against the DTD. e.g. @code XmlDocument myDocument (File ("myfile.xml")); ScopedPointer mainElement (myDocument.getDocumentElement()); if (mainElement == nullptr) { String error = myDocument.getLastParseError(); } else { ..use the element } @endcode Or you can use the static helper methods for quick parsing.. @code ScopedPointer xml (XmlDocument::parse (myXmlFile)); if (xml != nullptr && xml->hasTagName ("foobar")) { ...etc @endcode @see XmlElement */ class JUCE_API XmlDocument { public: //============================================================================== /** Creates an XmlDocument from the xml text. The text doesn't actually get parsed until the getDocumentElement() method is called. */ XmlDocument (const String& documentText); /** Creates an XmlDocument from a file. The text doesn't actually get parsed until the getDocumentElement() method is called. */ XmlDocument (const File& file); /** Destructor. */ ~XmlDocument(); //============================================================================== /** Creates an XmlElement object to represent the main document node. This method will do the actual parsing of the text, and if there's a parse error, it may returns nullptr (and you can find out the error using the getLastParseError() method). See also the parse() methods, which provide a shorthand way to quickly parse a file or string. @param onlyReadOuterDocumentElement if true, the parser will only read the first section of the file, and will only return the outer document element - this allows quick checking of large files to see if they contain the correct type of tag, without having to parse the entire file @returns a new XmlElement which the caller will need to delete, or null if there was an error. @see getLastParseError */ XmlElement* getDocumentElement (bool onlyReadOuterDocumentElement = false); /** Returns the parsing error that occurred the last time getDocumentElement was called. @returns the error, or an empty string if there was no error. */ const String& getLastParseError() const noexcept; /** Sets an input source object to use for parsing documents that reference external entities. If the document has been created from a file, this probably won't be needed, but if you're parsing some text and there might be a DTD that references external files, you may need to create a custom input source that can retrieve the other files it needs. The object that is passed-in will be deleted automatically when no longer needed. @see InputSource */ void setInputSource (InputSource* newSource) noexcept; /** Sets a flag to change the treatment of empty text elements. If this is true (the default state), then any text elements that contain only whitespace characters will be ingored during parsing. If you need to catch whitespace-only text, then you should set this to false before calling the getDocumentElement() method. */ void setEmptyTextElementsIgnored (bool shouldBeIgnored) noexcept; //============================================================================== /** A handy static method that parses a file. This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it. @returns a new XmlElement which the caller will need to delete, or null if there was an error. */ static XmlElement* parse (const File& file); /** A handy static method that parses some XML data. This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it. @returns a new XmlElement which the caller will need to delete, or null if there was an error. */ static XmlElement* parse (const String& xmlData); //============================================================================== private: String originalText; String::CharPointerType input; bool outOfData, errorOccurred; String lastError, dtdText; StringArray tokenisedDTD; bool needToLoadDTD, ignoreEmptyTextElements; ScopedPointer inputSource; XmlElement* parseDocumentElement (String::CharPointerType, bool outer); void setLastError (const String&, bool carryOn); bool parseHeader(); bool parseDTD(); void skipNextWhiteSpace(); juce_wchar readNextChar() noexcept; XmlElement* readNextElement (bool alsoParseSubElements); void readChildElements (XmlElement&); void readQuotedString (String&); void readEntity (String&); String getFileContents (const String&) const; String expandEntity (const String&); String expandExternalEntity (const String&); String getParameterEntity (const String&); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlDocument) }; #endif // JUCE_XMLDOCUMENT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp000066400000000000000000000653471320201440200301450ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept : name (other.name), value (other.value) { } XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept : name (n), value (v) { #if JUCE_DEBUG // this checks whether the attribute name string contains any illegal characters.. for (String::CharPointerType t (name.getCharPointer()); ! t.isEmpty(); ++t) jassert (t.isLetterOrDigit() || *t == '_' || *t == '-' || *t == ':'); #endif } XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd) : name (nameStart, nameEnd) { } //============================================================================== static void sanityCheckTagName (const String& tag) { (void) tag; // the tag name mustn't be empty, or it'll look like a text element! jassert (tag.containsNonWhitespaceChars()); // The tag can't contain spaces or other characters that would create invalid XML! jassert (! tag.containsAnyOf (" <>/&(){}")); } XmlElement::XmlElement (const String& tag) : tagName (StringPool::getGlobalPool().getPooledString (tag)) { sanityCheckTagName (tagName); } XmlElement::XmlElement (const char* tag) : tagName (StringPool::getGlobalPool().getPooledString (tag)) { sanityCheckTagName (tagName); } XmlElement::XmlElement (StringRef tag) : tagName (StringPool::getGlobalPool().getPooledString (tag)) { sanityCheckTagName (tagName); } XmlElement::XmlElement (const Identifier& tag) : tagName (tag.toString()) { sanityCheckTagName (tagName); } XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd) : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd)) { sanityCheckTagName (tagName); } XmlElement::XmlElement (int /*dummy*/) noexcept { } XmlElement::XmlElement (const XmlElement& other) : tagName (other.tagName) { copyChildrenAndAttributesFrom (other); } XmlElement& XmlElement::operator= (const XmlElement& other) { if (this != &other) { removeAllAttributes(); deleteAllChildElements(); tagName = other.tagName; copyChildrenAndAttributesFrom (other); } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS XmlElement::XmlElement (XmlElement&& other) noexcept : nextListItem (static_cast&&> (other.nextListItem)), firstChildElement (static_cast&&> (other.firstChildElement)), attributes (static_cast&&> (other.attributes)), tagName (static_cast (other.tagName)) { } XmlElement& XmlElement::operator= (XmlElement&& other) noexcept { jassert (this != &other); // hopefully the compiler should make this situation impossible! removeAllAttributes(); deleteAllChildElements(); nextListItem = static_cast&&> (other.nextListItem); firstChildElement = static_cast&&> (other.firstChildElement); attributes = static_cast&&> (other.attributes); tagName = static_cast (other.tagName); return *this; } #endif void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other) { jassert (firstChildElement.get() == nullptr); firstChildElement.addCopyOfList (other.firstChildElement); jassert (attributes.get() == nullptr); attributes.addCopyOfList (other.attributes); } XmlElement::~XmlElement() noexcept { firstChildElement.deleteAll(); attributes.deleteAll(); } //============================================================================== namespace XmlOutputFunctions { #if 0 // (These functions are just used to generate the lookup table used below) bool isLegalXmlCharSlow (const juce_wchar character) noexcept { if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9')) return true; const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|"; do { if (((juce_wchar) (uint8) *t) == character) return true; } while (*++t != 0); return false; } void generateLegalCharLookupTable() { uint8 n[32] = { 0 }; for (int i = 0; i < 256; ++i) if (isLegalXmlCharSlow (i)) n[i >> 3] |= (1 << (i & 7)); String s; for (int i = 0; i < 32; ++i) s << (int) n[i] << ", "; DBG (s); } #endif static bool isLegalXmlChar (const uint32 c) noexcept { static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, 255, 255, 191, 254, 255, 255, 127 }; return c < sizeof (legalChars) * 8 && (legalChars [c >> 3] & (1 << (c & 7))) != 0; } static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, const bool changeNewLines) { String::CharPointerType t (text.getCharPointer()); for (;;) { const uint32 character = (uint32) t.getAndAdvance(); if (character == 0) break; if (isLegalXmlChar (character)) { outputStream << (char) character; } else { switch (character) { case '&': outputStream << "&"; break; case '"': outputStream << """; break; case '>': outputStream << ">"; break; case '<': outputStream << "<"; break; case '\n': case '\r': if (! changeNewLines) { outputStream << (char) character; break; } // Note: deliberate fall-through here! default: outputStream << "&#" << ((int) character) << ';'; break; } } } } static void writeSpaces (OutputStream& out, const size_t numSpaces) { out.writeRepeatedByte (' ', numSpaces); } } void XmlElement::writeElementAsText (OutputStream& outputStream, const int indentationLevel, const int lineWrapLength) const { using namespace XmlOutputFunctions; if (indentationLevel >= 0) writeSpaces (outputStream, (size_t) indentationLevel); if (! isTextElement()) { outputStream.writeByte ('<'); outputStream << tagName; { const size_t attIndent = (size_t) (indentationLevel + tagName.length() + 1); int lineLen = 0; for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) { if (lineLen > lineWrapLength && indentationLevel >= 0) { outputStream << newLine; writeSpaces (outputStream, attIndent); lineLen = 0; } const int64 startPos = outputStream.getPosition(); outputStream.writeByte (' '); outputStream << att->name; outputStream.write ("=\"", 2); escapeIllegalXmlChars (outputStream, att->value, true); outputStream.writeByte ('"'); lineLen += (int) (outputStream.getPosition() - startPos); } } if (firstChildElement != nullptr) { outputStream.writeByte ('>'); bool lastWasTextNode = false; for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) { if (child->isTextElement()) { escapeIllegalXmlChars (outputStream, child->getText(), false); lastWasTextNode = true; } else { if (indentationLevel >= 0 && ! lastWasTextNode) outputStream << newLine; child->writeElementAsText (outputStream, lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength); lastWasTextNode = false; } } if (indentationLevel >= 0 && ! lastWasTextNode) { outputStream << newLine; writeSpaces (outputStream, (size_t) indentationLevel); } outputStream.write ("'); } else { outputStream.write ("/>", 2); } } else { escapeIllegalXmlChars (outputStream, getText(), false); } } String XmlElement::createDocument (StringRef dtdToUse, const bool allOnOneLine, const bool includeXmlHeader, StringRef encodingType, const int lineWrapLength) const { MemoryOutputStream mem (2048); writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength); return mem.toUTF8(); } void XmlElement::writeToStream (OutputStream& output, StringRef dtdToUse, const bool allOnOneLine, const bool includeXmlHeader, StringRef encodingType, const int lineWrapLength) const { using namespace XmlOutputFunctions; if (includeXmlHeader) { output << ""; if (allOnOneLine) output.writeByte (' '); else output << newLine << newLine; } if (dtdToUse.isNotEmpty()) { output << dtdToUse; if (allOnOneLine) output.writeByte (' '); else output << newLine; } writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength); if (! allOnOneLine) output << newLine; } bool XmlElement::writeToFile (const File& file, StringRef dtdToUse, StringRef encodingType, const int lineWrapLength) const { TemporaryFile tempFile (file); { FileOutputStream out (tempFile.getFile()); if (! out.openedOk()) return false; writeToStream (out, dtdToUse, false, true, encodingType, lineWrapLength); } return tempFile.overwriteTargetFileWithTemporary(); } //============================================================================== bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept { const bool matches = tagName.equalsIgnoreCase (possibleTagName); // XML tags should be case-sensitive, so although this method allows a // case-insensitive match to pass, you should try to avoid this. jassert ((! matches) || tagName == possibleTagName); return matches; } String XmlElement::getNamespace() const { return tagName.upToFirstOccurrenceOf (":", false, false); } String XmlElement::getTagNameWithoutNamespace() const { return tagName.fromLastOccurrenceOf (":", false, false); } bool XmlElement::hasTagNameIgnoringNamespace (StringRef possibleTagName) const { return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName; } XmlElement* XmlElement::getNextElementWithTagName (StringRef requiredTagName) const { XmlElement* e = nextListItem; while (e != nullptr && ! e->hasTagName (requiredTagName)) e = e->nextListItem; return e; } //============================================================================== int XmlElement::getNumAttributes() const noexcept { return attributes.size(); } const String& XmlElement::getAttributeName (const int index) const noexcept { if (const XmlAttributeNode* const att = attributes [index]) return att->name.toString(); return String::empty; } const String& XmlElement::getAttributeValue (const int index) const noexcept { if (const XmlAttributeNode* const att = attributes [index]) return att->value; return String::empty; } XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept { for (XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) if (att->name == attributeName) return att; return nullptr; } bool XmlElement::hasAttribute (StringRef attributeName) const noexcept { return getAttribute (attributeName) != nullptr; } //============================================================================== const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept { if (const XmlAttributeNode* att = getAttribute (attributeName)) return att->value; return String::empty; } String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const { if (const XmlAttributeNode* att = getAttribute (attributeName)) return att->value; return defaultReturnValue; } int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const { if (const XmlAttributeNode* att = getAttribute (attributeName)) return att->value.getIntValue(); return defaultReturnValue; } double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const { if (const XmlAttributeNode* att = getAttribute (attributeName)) return att->value.getDoubleValue(); return defaultReturnValue; } bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const { if (const XmlAttributeNode* att = getAttribute (attributeName)) { const juce_wchar firstChar = *(att->value.getCharPointer().findEndOfWhitespace()); return firstChar == '1' || firstChar == 't' || firstChar == 'y' || firstChar == 'T' || firstChar == 'Y'; } return defaultReturnValue; } bool XmlElement::compareAttribute (StringRef attributeName, StringRef stringToCompareAgainst, const bool ignoreCase) const noexcept { if (const XmlAttributeNode* att = getAttribute (attributeName)) return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst) : att->value == stringToCompareAgainst; return false; } //============================================================================== void XmlElement::setAttribute (const Identifier& attributeName, const String& value) { if (attributes == nullptr) { attributes = new XmlAttributeNode (attributeName, value); } else { for (XmlAttributeNode* att = attributes; ; att = att->nextListItem) { if (att->name == attributeName) { att->value = value; break; } if (att->nextListItem == nullptr) { att->nextListItem = new XmlAttributeNode (attributeName, value); break; } } } } void XmlElement::setAttribute (const Identifier& attributeName, const int number) { setAttribute (attributeName, String (number)); } void XmlElement::setAttribute (const Identifier& attributeName, const double number) { setAttribute (attributeName, String (number, 20)); } void XmlElement::removeAttribute (const Identifier& attributeName) noexcept { for (LinkedListPointer* att = &attributes; att->get() != nullptr; att = &(att->get()->nextListItem)) { if (att->get()->name == attributeName) { delete att->removeNext(); break; } } } void XmlElement::removeAllAttributes() noexcept { attributes.deleteAll(); } //============================================================================== int XmlElement::getNumChildElements() const noexcept { return firstChildElement.size(); } XmlElement* XmlElement::getChildElement (const int index) const noexcept { return firstChildElement [index].get(); } XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept { jassert (! childName.isEmpty()); for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) if (child->hasTagName (childName)) return child; return nullptr; } XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept { jassert (! attributeName.isEmpty()); for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) if (child->compareAttribute (attributeName, attributeValue)) return child; return nullptr; } void XmlElement::addChildElement (XmlElement* const newNode) noexcept { if (newNode != nullptr) { // The element being added must not be a child of another node! jassert (newNode->nextListItem == nullptr); firstChildElement.append (newNode); } } void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept { if (newNode != nullptr) { // The element being added must not be a child of another node! jassert (newNode->nextListItem == nullptr); firstChildElement.insertAtIndex (indexToInsertAt, newNode); } } void XmlElement::prependChildElement (XmlElement* newNode) noexcept { if (newNode != nullptr) { // The element being added must not be a child of another node! jassert (newNode->nextListItem == nullptr); firstChildElement.insertNext (newNode); } } XmlElement* XmlElement::createNewChildElement (StringRef childTagName) { XmlElement* const newElement = new XmlElement (childTagName); addChildElement (newElement); return newElement; } bool XmlElement::replaceChildElement (XmlElement* const currentChildElement, XmlElement* const newNode) noexcept { if (newNode != nullptr) { if (LinkedListPointer* const p = firstChildElement.findPointerTo (currentChildElement)) { if (currentChildElement != newNode) delete p->replaceNext (newNode); return true; } } return false; } void XmlElement::removeChildElement (XmlElement* const childToRemove, const bool shouldDeleteTheChild) noexcept { if (childToRemove != nullptr) { firstChildElement.remove (childToRemove); if (shouldDeleteTheChild) delete childToRemove; } } bool XmlElement::isEquivalentTo (const XmlElement* const other, const bool ignoreOrderOfAttributes) const noexcept { if (this != other) { if (other == nullptr || tagName != other->tagName) return false; if (ignoreOrderOfAttributes) { int totalAtts = 0; for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) { if (! other->compareAttribute (att->name, att->value)) return false; ++totalAtts; } if (totalAtts != other->getNumAttributes()) return false; } else { const XmlAttributeNode* thisAtt = attributes; const XmlAttributeNode* otherAtt = other->attributes; for (;;) { if (thisAtt == nullptr || otherAtt == nullptr) { if (thisAtt == otherAtt) // both nullptr, so it's a match break; return false; } if (thisAtt->name != otherAtt->name || thisAtt->value != otherAtt->value) { return false; } thisAtt = thisAtt->nextListItem; otherAtt = otherAtt->nextListItem; } } const XmlElement* thisChild = firstChildElement; const XmlElement* otherChild = other->firstChildElement; for (;;) { if (thisChild == nullptr || otherChild == nullptr) { if (thisChild == otherChild) // both 0, so it's a match break; return false; } if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes)) return false; thisChild = thisChild->nextListItem; otherChild = otherChild->nextListItem; } } return true; } void XmlElement::deleteAllChildElements() noexcept { firstChildElement.deleteAll(); } void XmlElement::deleteAllChildElementsWithTagName (StringRef name) noexcept { for (XmlElement* child = firstChildElement; child != nullptr;) { XmlElement* const nextChild = child->nextListItem; if (child->hasTagName (name)) removeChildElement (child, true); child = nextChild; } } bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept { return firstChildElement.contains (possibleChild); } XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept { if (this == elementToLookFor || elementToLookFor == nullptr) return nullptr; for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) { if (elementToLookFor == child) return this; if (XmlElement* const found = child->findParentElementOf (elementToLookFor)) return found; } return nullptr; } void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept { firstChildElement.copyToArray (elems); } void XmlElement::reorderChildElements (XmlElement** const elems, const int num) noexcept { XmlElement* e = firstChildElement = elems[0]; for (int i = 1; i < num; ++i) { e->nextListItem = elems[i]; e = e->nextListItem; } e->nextListItem = nullptr; } //============================================================================== bool XmlElement::isTextElement() const noexcept { return tagName.isEmpty(); } static const String juce_xmltextContentAttributeName ("text"); const String& XmlElement::getText() const noexcept { jassert (isTextElement()); // you're trying to get the text from an element that // isn't actually a text element.. If this contains text sub-nodes, you // probably want to use getAllSubText instead. return getStringAttribute (juce_xmltextContentAttributeName); } void XmlElement::setText (const String& newText) { if (isTextElement()) setAttribute (juce_xmltextContentAttributeName, newText); else jassertfalse; // you can only change the text in a text element, not a normal one. } String XmlElement::getAllSubText() const { if (isTextElement()) return getText(); if (getNumChildElements() == 1) return firstChildElement.get()->getAllSubText(); MemoryOutputStream mem (1024); for (const XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) mem << child->getAllSubText(); return mem.toUTF8(); } String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const { if (const XmlElement* const child = getChildByName (childTagName)) return child->getAllSubText(); return defaultReturnValue; } XmlElement* XmlElement::createTextElement (const String& text) { XmlElement* const e = new XmlElement ((int) 0); e->setAttribute (juce_xmltextContentAttributeName, text); return e; } void XmlElement::addTextElement (const String& text) { addChildElement (createTextElement (text)); } void XmlElement::deleteAllTextElements() noexcept { for (XmlElement* child = firstChildElement; child != nullptr;) { XmlElement* const next = child->nextListItem; if (child->isTextElement()) removeChildElement (child, true); child = next; } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h000066400000000000000000001022371320201440200276000ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_XMLELEMENT_H_INCLUDED #define JUCE_XMLELEMENT_H_INCLUDED //============================================================================== /** A handy macro to make it easy to iterate all the child elements in an XmlElement. The parentXmlElement should be a reference to the parent XML, and the childElementVariableName will be the name of a pointer to each child element. E.g. @code XmlElement* myParentXml = createSomeKindOfXmlDocument(); forEachXmlChildElement (*myParentXml, child) { if (child->hasTagName ("FOO")) doSomethingWithXmlElement (child); } @endcode @see forEachXmlChildElementWithTagName */ #define forEachXmlChildElement(parentXmlElement, childElementVariableName) \ \ for (juce::XmlElement* childElementVariableName = (parentXmlElement).getFirstChildElement(); \ childElementVariableName != nullptr; \ childElementVariableName = childElementVariableName->getNextElement()) /** A macro that makes it easy to iterate all the child elements of an XmlElement which have a specified tag. This does the same job as the forEachXmlChildElement macro, but only for those elements that have a particular tag name. The parentXmlElement should be a reference to the parent XML, and the childElementVariableName will be the name of a pointer to each child element. The requiredTagName is the tag name to match. E.g. @code XmlElement* myParentXml = createSomeKindOfXmlDocument(); forEachXmlChildElementWithTagName (*myParentXml, child, "MYTAG") { // the child object is now guaranteed to be a element.. doSomethingWithMYTAGElement (child); } @endcode @see forEachXmlChildElement */ #define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \ \ for (juce::XmlElement* childElementVariableName = (parentXmlElement).getChildByName (requiredTagName); \ childElementVariableName != nullptr; \ childElementVariableName = childElementVariableName->getNextElementWithTagName (requiredTagName)) //============================================================================== /** Used to build a tree of elements representing an XML document. An XML document can be parsed into a tree of XmlElements, each of which represents an XML tag structure, and which may itself contain other nested elements. An XmlElement can also be converted back into a text document, and has lots of useful methods for manipulating its attributes and sub-elements, so XmlElements can actually be used as a handy general-purpose data structure. Here's an example of parsing some elements: @code // check we're looking at the right kind of document.. if (myElement->hasTagName ("ANIMALS")) { // now we'll iterate its sub-elements looking for 'giraffe' elements.. forEachXmlChildElement (*myElement, e) { if (e->hasTagName ("GIRAFFE")) { // found a giraffe, so use some of its attributes.. String giraffeName = e->getStringAttribute ("name"); int giraffeAge = e->getIntAttribute ("age"); bool isFriendly = e->getBoolAttribute ("friendly"); } } } @endcode And here's an example of how to create an XML document from scratch: @code // create an outer node called "ANIMALS" XmlElement animalsList ("ANIMALS"); for (int i = 0; i < numAnimals; ++i) { // create an inner element.. XmlElement* giraffe = new XmlElement ("GIRAFFE"); giraffe->setAttribute ("name", "nigel"); giraffe->setAttribute ("age", 10); giraffe->setAttribute ("friendly", true); // ..and add our new element to the parent node animalsList.addChildElement (giraffe); } // now we can turn the whole thing into a text document.. String myXmlDoc = animalsList.createDocument (String::empty); @endcode @see XmlDocument */ class JUCE_API XmlElement { public: //============================================================================== /** Creates an XmlElement with this tag name. */ explicit XmlElement (const String& tagName); /** Creates an XmlElement with this tag name. */ explicit XmlElement (const char* tagName); /** Creates an XmlElement with this tag name. */ explicit XmlElement (const Identifier& tagName); /** Creates an XmlElement with this tag name. */ explicit XmlElement (StringRef tagName); /** Creates an XmlElement with this tag name. */ XmlElement (String::CharPointerType tagNameBegin, String::CharPointerType tagNameEnd); /** Creates a (deep) copy of another element. */ XmlElement (const XmlElement&); /** Creates a (deep) copy of another element. */ XmlElement& operator= (const XmlElement&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS XmlElement (XmlElement&&) noexcept; XmlElement& operator= (XmlElement&&) noexcept; #endif /** Deleting an XmlElement will also delete all of its child elements. */ ~XmlElement() noexcept; //============================================================================== /** Compares two XmlElements to see if they contain the same text and attiributes. The elements are only considered equivalent if they contain the same attiributes with the same values, and have the same sub-nodes. @param other the other element to compare to @param ignoreOrderOfAttributes if true, this means that two elements with the same attributes in a different order will be considered the same; if false, the attributes must be in the same order as well */ bool isEquivalentTo (const XmlElement* other, bool ignoreOrderOfAttributes) const noexcept; //============================================================================== /** Returns an XML text document that represents this element. The string returned can be parsed to recreate the same XmlElement that was used to create it. @param dtdToUse the DTD to add to the document @param allOnOneLine if true, this means that the document will not contain any linefeeds, so it'll be smaller but not very easy to read. @param includeXmlHeader whether to add the ", this would return "MOOSE". @see hasTagName */ const String& getTagName() const noexcept { return tagName; } /** Returns the namespace portion of the tag-name, or an empty string if none is specified. */ String getNamespace() const; /** Returns the part of the tag-name that follows any namespace declaration. */ String getTagNameWithoutNamespace() const; /** Tests whether this element has a particular tag name. @param possibleTagName the tag name you're comparing it with @see getTagName */ bool hasTagName (StringRef possibleTagName) const noexcept; /** Tests whether this element has a particular tag name, ignoring any XML namespace prefix. So a test for e.g. "xyz" will return true for "xyz" and also "foo:xyz", "bar::xyz", etc. @see getTagName */ bool hasTagNameIgnoringNamespace (StringRef possibleTagName) const; //============================================================================== /** Returns the number of XML attributes this element contains. E.g. for an element such as \, this would return 2. */ int getNumAttributes() const noexcept; /** Returns the name of one of the elements attributes. E.g. for an element such as \, then getAttributeName(1) would return "antlers". @see getAttributeValue, getStringAttribute */ const String& getAttributeName (int attributeIndex) const noexcept; /** Returns the value of one of the elements attributes. E.g. for an element such as \, then getAttributeName(1) would return "2". @see getAttributeName, getStringAttribute */ const String& getAttributeValue (int attributeIndex) const noexcept; //============================================================================== // Attribute-handling methods.. /** Checks whether the element contains an attribute with a certain name. */ bool hasAttribute (StringRef attributeName) const noexcept; /** Returns the value of a named attribute. @param attributeName the name of the attribute to look up */ const String& getStringAttribute (StringRef attributeName) const noexcept; /** Returns the value of a named attribute. @param attributeName the name of the attribute to look up @param defaultReturnValue a value to return if the element doesn't have an attribute with this name */ String getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const; /** Compares the value of a named attribute with a value passed-in. @param attributeName the name of the attribute to look up @param stringToCompareAgainst the value to compare it with @param ignoreCase whether the comparison should be case-insensitive @returns true if the value of the attribute is the same as the string passed-in; false if it's different (or if no such attribute exists) */ bool compareAttribute (StringRef attributeName, StringRef stringToCompareAgainst, bool ignoreCase = false) const noexcept; /** Returns the value of a named attribute as an integer. This will try to find the attribute and convert it to an integer (using the String::getIntValue() method). @param attributeName the name of the attribute to look up @param defaultReturnValue a value to return if the element doesn't have an attribute with this name @see setAttribute */ int getIntAttribute (StringRef attributeName, int defaultReturnValue = 0) const; /** Returns the value of a named attribute as floating-point. This will try to find the attribute and convert it to a double (using the String::getDoubleValue() method). @param attributeName the name of the attribute to look up @param defaultReturnValue a value to return if the element doesn't have an attribute with this name @see setAttribute */ double getDoubleAttribute (StringRef attributeName, double defaultReturnValue = 0.0) const; /** Returns the value of a named attribute as a boolean. This will try to find the attribute and interpret it as a boolean. To do this, it'll return true if the value is "1", "true", "y", etc, or false for other values. @param attributeName the name of the attribute to look up @param defaultReturnValue a value to return if the element doesn't have an attribute with this name */ bool getBoolAttribute (StringRef attributeName, bool defaultReturnValue = false) const; /** Adds a named attribute to the element. If the element already contains an attribute with this name, it's value will be updated to the new value. If there's no such attribute yet, a new one will be added. Note that there are other setAttribute() methods that take integers, doubles, etc. to make it easy to store numbers. @param attributeName the name of the attribute to set @param newValue the value to set it to @see removeAttribute */ void setAttribute (const Identifier& attributeName, const String& newValue); /** Adds a named attribute to the element, setting it to an integer value. If the element already contains an attribute with this name, it's value will be updated to the new value. If there's no such attribute yet, a new one will be added. Note that there are other setAttribute() methods that take integers, doubles, etc. to make it easy to store numbers. @param attributeName the name of the attribute to set @param newValue the value to set it to */ void setAttribute (const Identifier& attributeName, int newValue); /** Adds a named attribute to the element, setting it to a floating-point value. If the element already contains an attribute with this name, it's value will be updated to the new value. If there's no such attribute yet, a new one will be added. Note that there are other setAttribute() methods that take integers, doubles, etc. to make it easy to store numbers. @param attributeName the name of the attribute to set @param newValue the value to set it to */ void setAttribute (const Identifier& attributeName, double newValue); /** Removes a named attribute from the element. @param attributeName the name of the attribute to remove @see removeAllAttributes */ void removeAttribute (const Identifier& attributeName) noexcept; /** Removes all attributes from this element. */ void removeAllAttributes() noexcept; //============================================================================== // Child element methods.. /** Returns the first of this element's sub-elements. see getNextElement() for an example of how to iterate the sub-elements. @see forEachXmlChildElement */ XmlElement* getFirstChildElement() const noexcept { return firstChildElement; } /** Returns the next of this element's siblings. This can be used for iterating an element's sub-elements, e.g. @code XmlElement* child = myXmlDocument->getFirstChildElement(); while (child != nullptr) { ...do stuff with this child.. child = child->getNextElement(); } @endcode Note that when iterating the child elements, some of them might be text elements as well as XML tags - use isTextElement() to work this out. Also, it's much easier and neater to use this method indirectly via the forEachXmlChildElement macro. @returns the sibling element that follows this one, or zero if this is the last element in its parent @see getNextElement, isTextElement, forEachXmlChildElement */ inline XmlElement* getNextElement() const noexcept { return nextListItem; } /** Returns the next of this element's siblings which has the specified tag name. This is like getNextElement(), but will scan through the list until it finds an element with the given tag name. @see getNextElement, forEachXmlChildElementWithTagName */ XmlElement* getNextElementWithTagName (StringRef requiredTagName) const; /** Returns the number of sub-elements in this element. @see getChildElement */ int getNumChildElements() const noexcept; /** Returns the sub-element at a certain index. It's not very efficient to iterate the sub-elements by index - see getNextElement() for an example of how best to iterate. @returns the n'th child of this element, or nullptr if the index is out-of-range @see getNextElement, isTextElement, getChildByName */ XmlElement* getChildElement (int index) const noexcept; /** Returns the first sub-element with a given tag-name. @param tagNameToLookFor the tag name of the element you want to find @returns the first element with this tag name, or nullptr if none is found @see getNextElement, isTextElement, getChildElement, getChildByAttribute */ XmlElement* getChildByName (StringRef tagNameToLookFor) const noexcept; /** Returns the first sub-element which has an attribute that matches the given value. @param attributeName the name of the attribute to check @param attributeValue the target value of the attribute @returns the first element with this attribute value, or nullptr if none is found @see getChildByName */ XmlElement* getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept; //============================================================================== /** Appends an element to this element's list of children. Child elements are deleted automatically when their parent is deleted, so make sure the object that you pass in will not be deleted by anything else, and make sure it's not already the child of another element. Note that due to the XmlElement using a singly-linked-list, prependChildElement() is an O(1) operation, but addChildElement() is an O(N) operation - so if you're adding large number of elements, you may prefer to do so in reverse order! @see getFirstChildElement, getNextElement, getNumChildElements, getChildElement, removeChildElement */ void addChildElement (XmlElement* newChildElement) noexcept; /** Inserts an element into this element's list of children. Child elements are deleted automatically when their parent is deleted, so make sure the object that you pass in will not be deleted by anything else, and make sure it's not already the child of another element. @param newChildElement the element to add @param indexToInsertAt the index at which to insert the new element - if this is below zero, it will be added to the end of the list @see addChildElement, insertChildElement */ void insertChildElement (XmlElement* newChildElement, int indexToInsertAt) noexcept; /** Inserts an element at the beginning of this element's list of children. Child elements are deleted automatically when their parent is deleted, so make sure the object that you pass in will not be deleted by anything else, and make sure it's not already the child of another element. Note that due to the XmlElement using a singly-linked-list, prependChildElement() is an O(1) operation, but addChildElement() is an O(N) operation - so if you're adding large number of elements, you may prefer to do so in reverse order! @see addChildElement, insertChildElement */ void prependChildElement (XmlElement* newChildElement) noexcept; /** Creates a new element with the given name and returns it, after adding it as a child element. This is a handy method that means that instead of writing this: @code XmlElement* newElement = new XmlElement ("foobar"); myParentElement->addChildElement (newElement); @endcode ..you could just write this: @code XmlElement* newElement = myParentElement->createNewChildElement ("foobar"); @endcode */ XmlElement* createNewChildElement (StringRef tagName); /** Replaces one of this element's children with another node. If the current element passed-in isn't actually a child of this element, this will return false and the new one won't be added. Otherwise, the existing element will be deleted, replaced with the new one, and it will return true. */ bool replaceChildElement (XmlElement* currentChildElement, XmlElement* newChildNode) noexcept; /** Removes a child element. @param childToRemove the child to look for and remove @param shouldDeleteTheChild if true, the child will be deleted, if false it'll just remove it */ void removeChildElement (XmlElement* childToRemove, bool shouldDeleteTheChild) noexcept; /** Deletes all the child elements in the element. @see removeChildElement, deleteAllChildElementsWithTagName */ void deleteAllChildElements() noexcept; /** Deletes all the child elements with a given tag name. @see removeChildElement */ void deleteAllChildElementsWithTagName (StringRef tagName) noexcept; /** Returns true if the given element is a child of this one. */ bool containsChildElement (const XmlElement* possibleChild) const noexcept; /** Recursively searches all sub-elements of this one, looking for an element which is the direct parent of the specified element. Because elements don't store a pointer to their parent, if you have one and need to find its parent, the only way to do so is to exhaustively search the whole tree for it. If the given child is found somewhere in this element's hierarchy, then this method will return its parent. If not, it will return nullptr. */ XmlElement* findParentElementOf (const XmlElement* childToSearchFor) noexcept; //============================================================================== /** Sorts the child elements using a comparator. This will use a comparator object to sort the elements into order. The object passed must have a method of the form: @code int compareElements (const XmlElement* first, const XmlElement* second); @endcode ..and this method must return: - a value of < 0 if the first comes before the second - a value of 0 if the two objects are equivalent - a value of > 0 if the second comes before the first To improve performance, the compareElements() method can be declared as static or const. @param comparator the comparator to use for comparing elements. @param retainOrderOfEquivalentItems if this is true, then items which the comparator says are equivalent will be kept in the order in which they currently appear in the array. This is slower to perform, but may be important in some cases. If it's false, a faster algorithm is used, but equivalent elements may be rearranged. */ template void sortChildElements (ElementComparator& comparator, bool retainOrderOfEquivalentItems = false) { const int num = getNumChildElements(); if (num > 1) { HeapBlock elems ((size_t) num); getChildElementsAsArray (elems); sortArray (comparator, (XmlElement**) elems, 0, num - 1, retainOrderOfEquivalentItems); reorderChildElements (elems, num); } } //============================================================================== /** Returns true if this element is a section of text. Elements can either be an XML tag element or a secton of text, so this is used to find out what kind of element this one is. @see getAllText, addTextElement, deleteAllTextElements */ bool isTextElement() const noexcept; /** Returns the text for a text element. Note that if you have an element like this: @codehello@endcode then calling getText on the "xyz" element won't return "hello", because that is actually stored in a special text sub-element inside the xyz element. To get the "hello" string, you could either call getText on the (unnamed) sub-element, or use getAllSubText() to do this automatically. Note that leading and trailing whitespace will be included in the string - to remove if, just call String::trim() on the result. @see isTextElement, getAllSubText, getChildElementAllSubText */ const String& getText() const noexcept; /** Sets the text in a text element. Note that this is only a valid call if this element is a text element. If it's not, then no action will be performed. If you're trying to add text inside a normal element, you probably want to use addTextElement() instead. */ void setText (const String& newText); /** Returns all the text from this element's child nodes. This iterates all the child elements and when it finds text elements, it concatenates their text into a big string which it returns. E.g. @codehello there world@endcode if you called getAllSubText on the "xyz" element, it'd return "hello there world". Note that leading and trailing whitespace will be included in the string - to remove if, just call String::trim() on the result. @see isTextElement, getChildElementAllSubText, getText, addTextElement */ String getAllSubText() const; /** Returns all the sub-text of a named child element. If there is a child element with the given tag name, this will return all of its sub-text (by calling getAllSubText() on it). If there is no such child element, this will return the default string passed-in. @see getAllSubText */ String getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const; /** Appends a section of text to this element. @see isTextElement, getText, getAllSubText */ void addTextElement (const String& text); /** Removes all the text elements from this element. @see isTextElement, getText, getAllSubText, addTextElement */ void deleteAllTextElements() noexcept; /** Creates a text element that can be added to a parent element. */ static XmlElement* createTextElement (const String& text); //============================================================================== private: struct XmlAttributeNode { XmlAttributeNode (const XmlAttributeNode&) noexcept; XmlAttributeNode (const Identifier&, const String&) noexcept; XmlAttributeNode (String::CharPointerType, String::CharPointerType); LinkedListPointer nextListItem; Identifier name; String value; private: XmlAttributeNode& operator= (const XmlAttributeNode&) JUCE_DELETED_FUNCTION; }; friend class XmlDocument; friend class LinkedListPointer; friend class LinkedListPointer; friend class LinkedListPointer::Appender; friend class NamedValueSet; LinkedListPointer nextListItem; LinkedListPointer firstChildElement; LinkedListPointer attributes; String tagName; XmlElement (int) noexcept; void copyChildrenAndAttributesFrom (const XmlElement&); void writeElementAsText (OutputStream&, int indentationLevel, int lineWrapLength) const; void getChildElementsAsArray (XmlElement**) const noexcept; void reorderChildElements (XmlElement**, int) noexcept; XmlAttributeNode* getAttribute (StringRef) const noexcept; // Sigh.. L"" or _T("") string literals are problematic in general, and really inappropriate // for XML tags. Use a UTF-8 encoded literal instead, or if you're really determined to use // UTF-16, cast it to a String and use the other constructor. XmlElement (const wchar_t*) JUCE_DELETED_FUNCTION; JUCE_LEAK_DETECTOR (XmlElement) }; #endif // JUCE_XMLELEMENT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/zip/000077500000000000000000000000001320201440200243445ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp000066400000000000000000000157561320201440200333170ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ class GZIPCompressorOutputStream::GZIPCompressorHelper { public: GZIPCompressorHelper (const int compressionLevel, const int windowBits) : compLevel ((compressionLevel < 1 || compressionLevel > 9) ? -1 : compressionLevel), isFirstDeflate (true), streamIsValid (false), finished (false) { using namespace zlibNamespace; zerostruct (stream); streamIsValid = (deflateInit2 (&stream, compLevel, Z_DEFLATED, windowBits != 0 ? windowBits : MAX_WBITS, 8, strategy) == Z_OK); } ~GZIPCompressorHelper() { if (streamIsValid) zlibNamespace::deflateEnd (&stream); } bool write (const uint8* data, size_t dataSize, OutputStream& out) { // When you call flush() on a gzip stream, the stream is closed, and you can // no longer continue to write data to it! jassert (! finished); while (dataSize > 0) if (! doNextBlock (data, dataSize, out, Z_NO_FLUSH)) return false; return true; } void finish (OutputStream& out) { const uint8* data = nullptr; size_t dataSize = 0; while (! finished) doNextBlock (data, dataSize, out, Z_FINISH); } private: enum { strategy = 0 }; zlibNamespace::z_stream stream; const int compLevel; bool isFirstDeflate, streamIsValid, finished; zlibNamespace::Bytef buffer[32768]; bool doNextBlock (const uint8*& data, size_t& dataSize, OutputStream& out, const int flushMode) { using namespace zlibNamespace; if (streamIsValid) { stream.next_in = const_cast (data); stream.next_out = buffer; stream.avail_in = (z_uInt) dataSize; stream.avail_out = (z_uInt) sizeof (buffer); const int result = isFirstDeflate ? deflateParams (&stream, compLevel, strategy) : deflate (&stream, flushMode); isFirstDeflate = false; switch (result) { case Z_STREAM_END: finished = true; // Deliberate fall-through.. case Z_OK: { data += dataSize - stream.avail_in; dataSize = stream.avail_in; const ssize_t bytesDone = (ssize_t) sizeof (buffer) - (ssize_t) stream.avail_out; return bytesDone <= 0 || out.write (buffer, (size_t) bytesDone); } default: break; } } return false; } JUCE_DECLARE_NON_COPYABLE (GZIPCompressorHelper) }; //============================================================================== GZIPCompressorOutputStream::GZIPCompressorOutputStream (OutputStream* const out, const int compressionLevel, const bool deleteDestStream, const int windowBits) : destStream (out, deleteDestStream), helper (new GZIPCompressorHelper (compressionLevel, windowBits)) { jassert (out != nullptr); } GZIPCompressorOutputStream::~GZIPCompressorOutputStream() { flush(); } void GZIPCompressorOutputStream::flush() { helper->finish (*destStream); destStream->flush(); } bool GZIPCompressorOutputStream::write (const void* destBuffer, size_t howMany) { jassert (destBuffer != nullptr && (ssize_t) howMany >= 0); return helper->write (static_cast (destBuffer), howMany, *destStream); } int64 GZIPCompressorOutputStream::getPosition() { return destStream->getPosition(); } bool GZIPCompressorOutputStream::setPosition (int64 /*newPosition*/) { jassertfalse; // can't do it! return false; } //============================================================================== #if JUCE_UNIT_TESTS class GZIPTests : public UnitTest { public: GZIPTests() : UnitTest ("GZIP") {} void runTest() { beginTest ("GZIP"); Random rng = getRandom(); for (int i = 100; --i >= 0;) { MemoryOutputStream original, compressed, uncompressed; { GZIPCompressorOutputStream zipper (&compressed, rng.nextInt (10), false); for (int j = rng.nextInt (100); --j >= 0;) { MemoryBlock data ((unsigned int) (rng.nextInt (2000) + 1)); for (int k = (int) data.getSize(); --k >= 0;) data[k] = (char) rng.nextInt (255); original << data; zipper << data; } } { MemoryInputStream compressedInput (compressed.getData(), compressed.getDataSize(), false); GZIPDecompressorInputStream unzipper (compressedInput); uncompressed << unzipper; } expectEquals ((int) uncompressed.getDataSize(), (int) original.getDataSize()); if (original.getDataSize() == uncompressed.getDataSize()) expect (memcmp (uncompressed.getData(), original.getData(), original.getDataSize()) == 0); } } }; static GZIPTests gzipTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h000066400000000000000000000113021320201440200327430ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_GZIPCOMPRESSOROUTPUTSTREAM_H_INCLUDED #define JUCE_GZIPCOMPRESSOROUTPUTSTREAM_H_INCLUDED //============================================================================== /** A stream which uses zlib to compress the data written into it. Important note: When you call flush() on a GZIPCompressorOutputStream, the gzip data is closed - this means that no more data can be written to it, and any subsequent attempts to call write() will cause an assertion. @see GZIPDecompressorInputStream */ class JUCE_API GZIPCompressorOutputStream : public OutputStream { public: //============================================================================== /** Creates a compression stream. @param destStream the stream into which the compressed data should be written @param compressionLevel how much to compress the data, between 1 and 9, where 1 is the fastest/lowest compression, and 9 is the slowest/highest compression. Any value outside this range indicates that a default compression level should be used. @param deleteDestStreamWhenDestroyed whether or not to delete the destStream object when this stream is destroyed @param windowBits this is used internally to change the window size used by zlib - leave it as 0 unless you specifically need to set its value for some reason */ GZIPCompressorOutputStream (OutputStream* destStream, int compressionLevel = 0, bool deleteDestStreamWhenDestroyed = false, int windowBits = 0); /** Destructor. */ ~GZIPCompressorOutputStream(); //============================================================================== /** Flushes and closes the stream. Note that unlike most streams, when you call flush() on a GZIPCompressorOutputStream, the stream is closed - this means that no more data can be written to it, and any subsequent attempts to call write() will cause an assertion. */ void flush() override; int64 getPosition() override; bool setPosition (int64) override; bool write (const void*, size_t) override; /** These are preset values that can be used for the constructor's windowBits parameter. For more info about this, see the zlib documentation for its windowBits parameter. */ enum WindowBitsValues { windowBitsRaw = -15, windowBitsGZIP = 15 + 16 }; private: //============================================================================== OptionalScopedPointer destStream; class GZIPCompressorHelper; friend struct ContainerDeletePolicy; ScopedPointer helper; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPCompressorOutputStream) }; #endif // JUCE_GZIPCOMPRESSOROUTPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp000066400000000000000000000206671320201440200334240ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4309 4305 4365) #endif namespace zlibNamespace { #if JUCE_INCLUDE_ZLIB_CODE #if JUCE_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #undef OS_CODE #undef fdopen #define ZLIB_INTERNAL #define NO_DUMMY_DECL #include "zlib/zlib.h" #include "zlib/adler32.c" #include "zlib/compress.c" #undef DO1 #undef DO8 #include "zlib/crc32.c" #include "zlib/deflate.c" #include "zlib/inffast.c" #undef PULLBYTE #undef LOAD #undef RESTORE #undef INITBITS #undef NEEDBITS #undef DROPBITS #undef BYTEBITS #include "zlib/inflate.c" #include "zlib/inftrees.c" #include "zlib/trees.c" #include "zlib/zutil.c" #undef Byte #undef fdopen #undef local #undef Freq #undef Code #undef Dad #undef Len #if JUCE_CLANG #pragma clang diagnostic pop #endif #else #include JUCE_ZLIB_INCLUDE_PATH #endif } #if JUCE_MSVC #pragma warning (pop) #endif //============================================================================== // internal helper object that holds the zlib structures so they don't have to be // included publicly. class GZIPDecompressorInputStream::GZIPDecompressHelper { public: GZIPDecompressHelper (Format f) : finished (true), needsDictionary (false), error (true), streamIsValid (false), data (nullptr), dataSize (0) { using namespace zlibNamespace; zerostruct (stream); streamIsValid = (inflateInit2 (&stream, getBitsForFormat (f)) == Z_OK); finished = error = ! streamIsValid; } ~GZIPDecompressHelper() { using namespace zlibNamespace; if (streamIsValid) inflateEnd (&stream); } bool needsInput() const noexcept { return dataSize <= 0; } void setInput (uint8* const data_, const size_t size) noexcept { data = data_; dataSize = size; } int doNextBlock (uint8* const dest, const unsigned int destSize) { using namespace zlibNamespace; if (streamIsValid && data != nullptr && ! finished) { stream.next_in = data; stream.next_out = dest; stream.avail_in = (z_uInt) dataSize; stream.avail_out = (z_uInt) destSize; switch (inflate (&stream, Z_PARTIAL_FLUSH)) { case Z_STREAM_END: finished = true; // deliberate fall-through case Z_OK: data += dataSize - stream.avail_in; dataSize = (z_uInt) stream.avail_in; return (int) (destSize - stream.avail_out); case Z_NEED_DICT: needsDictionary = true; data += dataSize - stream.avail_in; dataSize = (size_t) stream.avail_in; break; case Z_DATA_ERROR: case Z_MEM_ERROR: error = true; default: break; } } return 0; } static int getBitsForFormat (Format f) noexcept { switch (f) { case zlibFormat: return MAX_WBITS; case deflateFormat: return -MAX_WBITS; case gzipFormat: return MAX_WBITS | 16; default: jassertfalse; break; } return MAX_WBITS; } bool finished, needsDictionary, error, streamIsValid; enum { gzipDecompBufferSize = 32768 }; private: zlibNamespace::z_stream stream; uint8* data; size_t dataSize; JUCE_DECLARE_NON_COPYABLE (GZIPDecompressHelper) }; //============================================================================== GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* source, bool deleteSourceWhenDestroyed, Format f, int64 uncompressedLength) : sourceStream (source, deleteSourceWhenDestroyed), uncompressedStreamLength (uncompressedLength), format (f), isEof (false), activeBufferSize (0), originalSourcePos (source->getPosition()), currentPos (0), buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), helper (new GZIPDecompressHelper (f)) { } GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream& source) : sourceStream (&source, false), uncompressedStreamLength (-1), format (zlibFormat), isEof (false), activeBufferSize (0), originalSourcePos (source.getPosition()), currentPos (0), buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), helper (new GZIPDecompressHelper (zlibFormat)) { } GZIPDecompressorInputStream::~GZIPDecompressorInputStream() { } int64 GZIPDecompressorInputStream::getTotalLength() { return uncompressedStreamLength; } int GZIPDecompressorInputStream::read (void* destBuffer, int howMany) { jassert (destBuffer != nullptr && howMany >= 0); if (howMany > 0 && ! isEof) { int numRead = 0; uint8* d = static_cast (destBuffer); while (! helper->error) { const int n = helper->doNextBlock (d, (unsigned int) howMany); currentPos += n; if (n == 0) { if (helper->finished || helper->needsDictionary) { isEof = true; return numRead; } if (helper->needsInput()) { activeBufferSize = sourceStream->read (buffer, (int) GZIPDecompressHelper::gzipDecompBufferSize); if (activeBufferSize > 0) { helper->setInput (buffer, (size_t) activeBufferSize); } else { isEof = true; return numRead; } } } else { numRead += n; howMany -= n; d += n; if (howMany <= 0) return numRead; } } } return 0; } bool GZIPDecompressorInputStream::isExhausted() { return helper->error || isEof; } int64 GZIPDecompressorInputStream::getPosition() { return currentPos; } bool GZIPDecompressorInputStream::setPosition (int64 newPos) { if (newPos < currentPos) { // to go backwards, reset the stream and start again.. isEof = false; activeBufferSize = 0; currentPos = 0; helper = new GZIPDecompressHelper (format); sourceStream->setPosition (originalSourcePos); } skipNextBytes (newPos - currentPos); return true; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h000066400000000000000000000111741320201440200330620ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_GZIPDECOMPRESSORINPUTSTREAM_H_INCLUDED #define JUCE_GZIPDECOMPRESSORINPUTSTREAM_H_INCLUDED //============================================================================== /** This stream will decompress a source-stream using zlib. Tip: if you're reading lots of small items from one of these streams, you can increase the performance enormously by passing it through a BufferedInputStream, so that it has to read larger blocks less often. @see GZIPCompressorOutputStream */ class JUCE_API GZIPDecompressorInputStream : public InputStream { public: enum Format { zlibFormat = 0, deflateFormat, gzipFormat }; //============================================================================== /** Creates a decompressor stream. @param sourceStream the stream to read from @param deleteSourceWhenDestroyed whether or not to delete the source stream when this object is destroyed @param sourceFormat can be used to select which of the supported formats the data is expected to be in @param uncompressedStreamLength if the creator knows the length that the uncompressed stream will be, then it can supply this value, which will be returned by getTotalLength() */ GZIPDecompressorInputStream (InputStream* sourceStream, bool deleteSourceWhenDestroyed, Format sourceFormat = zlibFormat, int64 uncompressedStreamLength = -1); /** Creates a decompressor stream. @param sourceStream the stream to read from - the source stream must not be deleted until this object has been destroyed */ GZIPDecompressorInputStream (InputStream& sourceStream); /** Destructor. */ ~GZIPDecompressorInputStream(); //============================================================================== int64 getPosition() override; bool setPosition (int64 pos) override; int64 getTotalLength() override; bool isExhausted() override; int read (void* destBuffer, int maxBytesToRead) override; private: //============================================================================== OptionalScopedPointer sourceStream; const int64 uncompressedStreamLength; const Format format; bool isEof; int activeBufferSize; int64 originalSourcePos, currentPos; HeapBlock buffer; class GZIPDecompressHelper; friend struct ContainerDeletePolicy; ScopedPointer helper; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // The arguments to this method have changed! Please pass a Format enum instead of the old dontWrap bool. GZIPDecompressorInputStream (InputStream*, bool, bool, int64 x = -1); #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream) }; #endif // JUCE_GZIPDECOMPRESSORINPUTSTREAM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp000066400000000000000000000450731320201440200274310ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ class ZipFile::ZipEntryHolder { public: ZipEntryHolder (const char* const buffer, const int fileNameLen) { entry.filename = String::fromUTF8 (buffer + 46, fileNameLen); const int time = ByteOrder::littleEndianShort (buffer + 12); const int date = ByteOrder::littleEndianShort (buffer + 14); entry.fileTime = getFileTimeFromRawEncodings (time, date); compressed = ByteOrder::littleEndianShort (buffer + 10) != 0; compressedSize = (size_t) ByteOrder::littleEndianInt (buffer + 20); entry.uncompressedSize = ByteOrder::littleEndianInt (buffer + 24); streamOffset = ByteOrder::littleEndianInt (buffer + 42); } struct FileNameComparator { static int compareElements (const ZipEntryHolder* first, const ZipEntryHolder* second) { return first->entry.filename.compare (second->entry.filename); } }; ZipEntry entry; size_t streamOffset; size_t compressedSize; bool compressed; private: static Time getFileTimeFromRawEncodings (int time, int date) { const int year = 1980 + (date >> 9); const int month = ((date >> 5) & 15) - 1; const int day = date & 31; const int hours = time >> 11; const int minutes = (time >> 5) & 63; const int seconds = (time & 31) << 1; return Time (year, month, day, hours, minutes, seconds); } }; //============================================================================== namespace { int findEndOfZipEntryTable (InputStream& input, int& numEntries) { BufferedInputStream in (input, 8192); in.setPosition (in.getTotalLength()); int64 pos = in.getPosition(); const int64 lowestPos = jmax ((int64) 0, pos - 1024); char buffer [32] = { 0 }; while (pos > lowestPos) { in.setPosition (pos - 22); pos = in.getPosition(); memcpy (buffer + 22, buffer, 4); if (in.read (buffer, 22) != 22) return 0; for (int i = 0; i < 22; ++i) { if (ByteOrder::littleEndianInt (buffer + i) == 0x06054b50) { in.setPosition (pos + i); in.read (buffer, 22); numEntries = ByteOrder::littleEndianShort (buffer + 10); return (int) ByteOrder::littleEndianInt (buffer + 16); } } } return 0; } } //============================================================================== class ZipFile::ZipInputStream : public InputStream { public: ZipInputStream (ZipFile& zf, ZipFile::ZipEntryHolder& zei) : file (zf), zipEntryHolder (zei), pos (0), headerSize (0), inputStream (zf.inputStream) { if (zf.inputSource != nullptr) { inputStream = streamToDelete = file.inputSource->createInputStream(); } else { #if JUCE_DEBUG zf.streamCounter.numOpenStreams++; #endif } char buffer [30]; if (inputStream != nullptr && inputStream->setPosition ((int64) zei.streamOffset) && inputStream->read (buffer, 30) == 30 && ByteOrder::littleEndianInt (buffer) == 0x04034b50) { headerSize = 30 + ByteOrder::littleEndianShort (buffer + 26) + ByteOrder::littleEndianShort (buffer + 28); } } ~ZipInputStream() { #if JUCE_DEBUG if (inputStream != nullptr && inputStream == file.inputStream) file.streamCounter.numOpenStreams--; #endif } int64 getTotalLength() { return (int64) zipEntryHolder.compressedSize; } int read (void* buffer, int howMany) { if (headerSize <= 0) return 0; howMany = (int) jmin ((int64) howMany, ((int64) zipEntryHolder.compressedSize) - pos); if (inputStream == nullptr) return 0; int num; if (inputStream == file.inputStream) { const ScopedLock sl (file.lock); inputStream->setPosition (pos + (int64) zipEntryHolder.streamOffset + headerSize); num = inputStream->read (buffer, howMany); } else { inputStream->setPosition (pos + (int64) zipEntryHolder.streamOffset + headerSize); num = inputStream->read (buffer, howMany); } pos += num; return num; } bool isExhausted() { return headerSize <= 0 || pos >= (int64) zipEntryHolder.compressedSize; } int64 getPosition() { return pos; } bool setPosition (int64 newPos) { pos = jlimit ((int64) 0, (int64) zipEntryHolder.compressedSize, newPos); return true; } private: ZipFile& file; ZipEntryHolder zipEntryHolder; int64 pos; int headerSize; InputStream* inputStream; ScopedPointer streamToDelete; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ZipInputStream) }; //============================================================================== ZipFile::ZipFile (InputStream* const stream, const bool deleteStreamWhenDestroyed) : inputStream (stream) { if (deleteStreamWhenDestroyed) streamToDelete = inputStream; init(); } ZipFile::ZipFile (InputStream& stream) : inputStream (&stream) { init(); } ZipFile::ZipFile (const File& file) : inputStream (nullptr), inputSource (new FileInputSource (file)) { init(); } ZipFile::ZipFile (InputSource* const source) : inputStream (nullptr), inputSource (source) { init(); } ZipFile::~ZipFile() { entries.clear(); } #if JUCE_DEBUG ZipFile::OpenStreamCounter::~OpenStreamCounter() { /* If you hit this assertion, it means you've created a stream to read one of the items in the zipfile, but you've forgotten to delete that stream object before deleting the file.. Streams can't be kept open after the file is deleted because they need to share the input stream that is managed by the ZipFile object. */ jassert (numOpenStreams == 0); } #endif //============================================================================== int ZipFile::getNumEntries() const noexcept { return entries.size(); } const ZipFile::ZipEntry* ZipFile::getEntry (const int index) const noexcept { if (ZipEntryHolder* const zei = entries [index]) return &(zei->entry); return nullptr; } int ZipFile::getIndexOfFileName (const String& fileName) const noexcept { for (int i = 0; i < entries.size(); ++i) if (entries.getUnchecked (i)->entry.filename == fileName) return i; return -1; } const ZipFile::ZipEntry* ZipFile::getEntry (const String& fileName) const noexcept { return getEntry (getIndexOfFileName (fileName)); } InputStream* ZipFile::createStreamForEntry (const int index) { InputStream* stream = nullptr; if (ZipEntryHolder* const zei = entries[index]) { stream = new ZipInputStream (*this, *zei); if (zei->compressed) { stream = new GZIPDecompressorInputStream (stream, true, GZIPDecompressorInputStream::deflateFormat, (int64) zei->entry.uncompressedSize); // (much faster to unzip in big blocks using a buffer..) stream = new BufferedInputStream (stream, 32768, true); } } return stream; } InputStream* ZipFile::createStreamForEntry (const ZipEntry& entry) { for (int i = 0; i < entries.size(); ++i) if (&entries.getUnchecked (i)->entry == &entry) return createStreamForEntry (i); return nullptr; } void ZipFile::sortEntriesByFilename() { ZipEntryHolder::FileNameComparator sorter; entries.sort (sorter); } //============================================================================== void ZipFile::init() { ScopedPointer toDelete; InputStream* in = inputStream; if (inputSource != nullptr) { in = inputSource->createInputStream(); toDelete = in; } if (in != nullptr) { int numEntries = 0; int pos = findEndOfZipEntryTable (*in, numEntries); if (pos >= 0 && pos < in->getTotalLength()) { const int size = (int) (in->getTotalLength() - pos); in->setPosition (pos); MemoryBlock headerData; if (in->readIntoMemoryBlock (headerData, size) == (size_t) size) { pos = 0; for (int i = 0; i < numEntries; ++i) { if (pos + 46 > size) break; const char* const buffer = static_cast (headerData.getData()) + pos; const int fileNameLen = ByteOrder::littleEndianShort (buffer + 28); if (pos + 46 + fileNameLen > size) break; entries.add (new ZipEntryHolder (buffer, fileNameLen)); pos += 46 + fileNameLen + ByteOrder::littleEndianShort (buffer + 30) + ByteOrder::littleEndianShort (buffer + 32); } } } } } Result ZipFile::uncompressTo (const File& targetDirectory, const bool shouldOverwriteFiles) { for (int i = 0; i < entries.size(); ++i) { Result result (uncompressEntry (i, targetDirectory, shouldOverwriteFiles)); if (result.failed()) return result; } return Result::ok(); } Result ZipFile::uncompressEntry (const int index, const File& targetDirectory, bool shouldOverwriteFiles) { const ZipEntryHolder* zei = entries.getUnchecked (index); #if JUCE_WINDOWS const String entryPath (zei->entry.filename); #else const String entryPath (zei->entry.filename.replaceCharacter ('\\', '/')); #endif const File targetFile (targetDirectory.getChildFile (entryPath)); if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\')) return targetFile.createDirectory(); // (entry is a directory, not a file) ScopedPointer in (createStreamForEntry (index)); if (in == nullptr) return Result::fail ("Failed to open the zip file for reading"); if (targetFile.exists()) { if (! shouldOverwriteFiles) return Result::ok(); if (! targetFile.deleteFile()) return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName()); } if (! targetFile.getParentDirectory().createDirectory()) return Result::fail ("Failed to create target folder: " + targetFile.getParentDirectory().getFullPathName()); { FileOutputStream out (targetFile); if (out.failedToOpen()) return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName()); out << *in; } targetFile.setCreationTime (zei->entry.fileTime); targetFile.setLastModificationTime (zei->entry.fileTime); targetFile.setLastAccessTime (zei->entry.fileTime); return Result::ok(); } //============================================================================= class ZipFile::Builder::Item { public: Item (const File& f, InputStream* s, const int compression, const String& storedPath, Time time) : file (f), stream (s), storedPathname (storedPath), fileTime (time), compressionLevel (compression), compressedSize (0), uncompressedSize (0), headerStart (0), checksum (0) { } bool writeData (OutputStream& target, const int64 overallStartPosition) { MemoryOutputStream compressedData ((size_t) file.getSize()); if (compressionLevel > 0) { GZIPCompressorOutputStream compressor (&compressedData, compressionLevel, false, GZIPCompressorOutputStream::windowBitsRaw); if (! writeSource (compressor)) return false; } else { if (! writeSource (compressedData)) return false; } compressedSize = (int) compressedData.getDataSize(); headerStart = (int) (target.getPosition() - overallStartPosition); target.writeInt (0x04034b50); writeFlagsAndSizes (target); target << storedPathname << compressedData; return true; } bool writeDirectoryEntry (OutputStream& target) { target.writeInt (0x02014b50); target.writeShort (20); // version written writeFlagsAndSizes (target); target.writeShort (0); // comment length target.writeShort (0); // start disk num target.writeShort (0); // internal attributes target.writeInt (0); // external attributes target.writeInt (headerStart); target << storedPathname; return true; } private: const File file; ScopedPointer stream; String storedPathname; Time fileTime; int compressionLevel, compressedSize, uncompressedSize, headerStart; unsigned long checksum; static void writeTimeAndDate (OutputStream& target, Time t) { target.writeShort ((short) (t.getSeconds() + (t.getMinutes() << 5) + (t.getHours() << 11))); target.writeShort ((short) (t.getDayOfMonth() + ((t.getMonth() + 1) << 5) + ((t.getYear() - 1980) << 9))); } bool writeSource (OutputStream& target) { if (stream == nullptr) { stream = file.createInputStream(); if (stream == nullptr) return false; } checksum = 0; uncompressedSize = 0; const int bufferSize = 4096; HeapBlock buffer (bufferSize); while (! stream->isExhausted()) { const int bytesRead = stream->read (buffer, bufferSize); if (bytesRead < 0) return false; checksum = zlibNamespace::crc32 (checksum, buffer, (unsigned int) bytesRead); target.write (buffer, (size_t) bytesRead); uncompressedSize += bytesRead; } stream = nullptr; return true; } void writeFlagsAndSizes (OutputStream& target) const { target.writeShort (10); // version needed target.writeShort ((short) (1 << 11)); // this flag indicates UTF-8 filename encoding target.writeShort (compressionLevel > 0 ? (short) 8 : (short) 0); writeTimeAndDate (target, fileTime); target.writeInt ((int) checksum); target.writeInt (compressedSize); target.writeInt (uncompressedSize); target.writeShort ((short) storedPathname.toUTF8().sizeInBytes() - 1); target.writeShort (0); // extra field length } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Item) }; //============================================================================= ZipFile::Builder::Builder() {} ZipFile::Builder::~Builder() {} void ZipFile::Builder::addFile (const File& file, const int compression, const String& path) { items.add (new Item (file, nullptr, compression, path.isEmpty() ? file.getFileName() : path, file.getLastModificationTime())); } void ZipFile::Builder::addEntry (InputStream* stream, int compression, const String& path, Time time) { jassert (stream != nullptr); // must not be null! jassert (path.isNotEmpty()); items.add (new Item (File(), stream, compression, path, time)); } bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progress) const { const int64 fileStart = target.getPosition(); for (int i = 0; i < items.size(); ++i) { if (progress != nullptr) *progress = (i + 0.5) / items.size(); if (! items.getUnchecked (i)->writeData (target, fileStart)) return false; } const int64 directoryStart = target.getPosition(); for (int i = 0; i < items.size(); ++i) if (! items.getUnchecked (i)->writeDirectoryEntry (target)) return false; const int64 directoryEnd = target.getPosition(); target.writeInt (0x06054b50); target.writeShort (0); target.writeShort (0); target.writeShort ((short) items.size()); target.writeShort ((short) items.size()); target.writeInt ((int) (directoryEnd - directoryStart)); target.writeInt ((int) (directoryStart - fileStart)); target.writeShort (0); if (progress != nullptr) *progress = 1.0; return true; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h000066400000000000000000000255301320201440200270720ustar00rootroot00000000000000/* ============================================================================== This file is part of the juce_core module of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ------------------------------------------------------------------------------ NOTE! This permissive ISC license applies ONLY to files within the juce_core module! All other JUCE modules are covered by a dual GPL/commercial license, so if you are using any other modules, be sure to check that you also comply with their license. For more details, visit www.juce.com ============================================================================== */ #ifndef JUCE_ZIPFILE_H_INCLUDED #define JUCE_ZIPFILE_H_INCLUDED //============================================================================== /** Decodes a ZIP file from a stream. This can enumerate the items in a ZIP file and can create suitable stream objects to read each one. */ class JUCE_API ZipFile { public: /** Creates a ZipFile to read a specific file. */ explicit ZipFile (const File& file); //============================================================================== /** Creates a ZipFile for a given stream. @param inputStream the stream to read from @param deleteStreamWhenDestroyed if set to true, the object passed-in will be deleted when this ZipFile object is deleted */ ZipFile (InputStream* inputStream, bool deleteStreamWhenDestroyed); /** Creates a ZipFile for a given stream. The stream will not be owned or deleted by this class - if you want the ZipFile to manage the stream's lifetime, use the other constructor. */ explicit ZipFile (InputStream& inputStream); /** Creates a ZipFile for an input source. The inputSource object will be owned by the zip file, which will delete it later when not needed. */ explicit ZipFile (InputSource* inputSource); /** Destructor. */ ~ZipFile(); //============================================================================== /** Contains information about one of the entries in a ZipFile. @see ZipFile::getEntry */ struct ZipEntry { /** The name of the file, which may also include a partial pathname. */ String filename; /** The file's original size. */ unsigned int uncompressedSize; /** The last time the file was modified. */ Time fileTime; }; //============================================================================== /** Returns the number of items in the zip file. */ int getNumEntries() const noexcept; /** Returns a structure that describes one of the entries in the zip file. This may return zero if the index is out of range. @see ZipFile::ZipEntry */ const ZipEntry* getEntry (int index) const noexcept; /** Returns the index of the first entry with a given filename. This uses a case-sensitive comparison to look for a filename in the list of entries. It might return -1 if no match is found. @see ZipFile::ZipEntry */ int getIndexOfFileName (const String& fileName) const noexcept; /** Returns a structure that describes one of the entries in the zip file. This uses a case-sensitive comparison to look for a filename in the list of entries. It might return 0 if no match is found. @see ZipFile::ZipEntry */ const ZipEntry* getEntry (const String& fileName) const noexcept; /** Sorts the list of entries, based on the filename. */ void sortEntriesByFilename(); //============================================================================== /** Creates a stream that can read from one of the zip file's entries. The stream that is returned must be deleted by the caller (and zero might be returned if a stream can't be opened for some reason). The stream must not be used after the ZipFile object that created has been deleted. Note that if the ZipFile was created with a user-supplied InputStream object, then all the streams which are created by this method will by trying to share the same source stream, so cannot be safely used on multiple threads! (But if you create the ZipFile from a File or InputSource, then it is safe to do this). */ InputStream* createStreamForEntry (int index); /** Creates a stream that can read from one of the zip file's entries. The stream that is returned must be deleted by the caller (and zero might be returned if a stream can't be opened for some reason). The stream must not be used after the ZipFile object that created has been deleted. Note that if the ZipFile was created with a user-supplied InputStream object, then all the streams which are created by this method will by trying to share the same source stream, so cannot be safely used on multiple threads! (But if you create the ZipFile from a File or InputSource, then it is safe to do this). */ InputStream* createStreamForEntry (const ZipEntry& entry); //============================================================================== /** Uncompresses all of the files in the zip file. This will expand all the entries into a target directory. The relative paths of the entries are used. @param targetDirectory the root folder to uncompress to @param shouldOverwriteFiles whether to overwrite existing files with similarly-named ones @returns success if the file is successfully unzipped */ Result uncompressTo (const File& targetDirectory, bool shouldOverwriteFiles = true); /** Uncompresses one of the entries from the zip file. This will expand the entry and write it in a target directory. The entry's path is used to determine which subfolder of the target should contain the new file. @param index the index of the entry to uncompress - this must be a valid index between 0 and (getNumEntries() - 1). @param targetDirectory the root folder to uncompress into @param shouldOverwriteFiles whether to overwrite existing files with similarly-named ones @returns success if all the files are successfully unzipped */ Result uncompressEntry (int index, const File& targetDirectory, bool shouldOverwriteFiles = true); //============================================================================== /** Used to create a new zip file. Create a ZipFile::Builder object, and call its addFile() method to add some files, then you can write it to a stream with write(). Currently this just stores the files with no compression.. That will be added soon! */ class Builder { public: Builder(); ~Builder(); /** Adds a file while should be added to the archive. The file isn't read immediately, all the files will be read later when the writeToStream() method is called. The compressionLevel can be between 0 (no compression), and 9 (maximum compression). If the storedPathName parameter is specified, you can customise the partial pathname that will be stored for this file. */ void addFile (const File& fileToAdd, int compressionLevel, const String& storedPathName = String()); /** Adds a file while should be added to the archive. @param streamToRead this stream isn't read immediately - a pointer to the stream is stored, then used later when the writeToStream() method is called, and deleted by the Builder object when no longer needed, so be very careful about its lifetime and the lifetime of any objects on which it depends! This must not be null. @param compressionLevel this can be between 0 (no compression), and 9 (maximum compression). @param storedPathName the partial pathname that will be stored for this file @param fileModificationTime the timestamp that will be stored as the last modification time of this entry */ void addEntry (InputStream* streamToRead, int compressionLevel, const String& storedPathName, Time fileModificationTime); /** Generates the zip file, writing it to the specified stream. If the progress parameter is non-null, it will be updated with an approximate progress status between 0 and 1.0 */ bool writeToStream (OutputStream& target, double* progress) const; //============================================================================== private: class Item; friend struct ContainerDeletePolicy; OwnedArray items; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Builder) }; private: //============================================================================== class ZipInputStream; class ZipEntryHolder; friend class ZipInputStream; friend class ZipEntryHolder; OwnedArray entries; CriticalSection lock; InputStream* inputStream; ScopedPointer streamToDelete; ScopedPointer inputSource; #if JUCE_DEBUG struct OpenStreamCounter { OpenStreamCounter() : numOpenStreams (0) {} ~OpenStreamCounter(); int numOpenStreams; }; OpenStreamCounter streamCounter; #endif void init(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ZipFile) }; #endif // JUCE_ZIPFILE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/000077500000000000000000000000001320201440200260265ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/app_properties/000077500000000000000000000000001320201440200310625ustar00rootroot00000000000000juce_ApplicationProperties.cpp000066400000000000000000000055521320201440200370440ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/app_properties/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ApplicationProperties::ApplicationProperties() : commonSettingsAreReadOnly (0) { } ApplicationProperties::~ApplicationProperties() { closeFiles(); } //============================================================================== void ApplicationProperties::setStorageParameters (const PropertiesFile::Options& newOptions) { options = newOptions; } //============================================================================== void ApplicationProperties::openFiles() { // You need to call setStorageParameters() before trying to get hold of the properties! jassert (options.applicationName.isNotEmpty()); if (options.applicationName.isNotEmpty()) { PropertiesFile::Options o (options); if (userProps == nullptr) { o.commonToAllUsers = false; userProps = new PropertiesFile (o); } if (commonProps == nullptr) { o.commonToAllUsers = true; commonProps = new PropertiesFile (o); } userProps->setFallbackPropertySet (commonProps); } } PropertiesFile* ApplicationProperties::getUserSettings() { if (userProps == nullptr) openFiles(); return userProps; } PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly) { if (commonProps == nullptr) openFiles(); if (returnUserPropsIfReadOnly) { if (commonSettingsAreReadOnly == 0) commonSettingsAreReadOnly = commonProps->save() ? -1 : 1; if (commonSettingsAreReadOnly > 0) return userProps; } return commonProps; } bool ApplicationProperties::saveIfNeeded() { return (userProps == nullptr || userProps->saveIfNeeded()) && (commonProps == nullptr || commonProps->saveIfNeeded()); } void ApplicationProperties::closeFiles() { userProps = nullptr; commonProps = nullptr; } juce_ApplicationProperties.h000066400000000000000000000124021320201440200365010ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/app_properties/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_APPLICATIONPROPERTIES_H_INCLUDED #define JUCE_APPLICATIONPROPERTIES_H_INCLUDED //============================================================================== /** Manages a collection of properties. This is a slightly higher-level wrapper for managing PropertiesFile objects. It holds two different PropertiesFile objects internally, one for user-specific settings (stored in your user directory), and one for settings that are common to all users (stored in a folder accessible to all users). The class manages the creation of these files on-demand, allowing access via the getUserSettings() and getCommonSettings() methods. After creating an instance of an ApplicationProperties object, you should first of all call setStorageParameters() to tell it the parameters to use to create its files. @see PropertiesFile */ class JUCE_API ApplicationProperties { public: //============================================================================== /** Creates an ApplicationProperties object. Before using it, you must call setStorageParameters() to give it the info it needs to create the property files. */ ApplicationProperties(); /** Destructor. */ ~ApplicationProperties(); //============================================================================== /** Gives the object the information it needs to create the appropriate properties files. See the PropertiesFile::Options class for details about what options you need to set. */ void setStorageParameters (const PropertiesFile::Options& options); /** Returns the current storage parameters. @see setStorageParameters */ const PropertiesFile::Options& getStorageParameters() const noexcept { return options; } //============================================================================== /** Returns the user settings file. The first time this is called, it will create and load the properties file. Note that when you search the user PropertiesFile for a value that it doesn't contain, the common settings are used as a second-chance place to look. This is done via the PropertySet::setFallbackPropertySet() method - by default the common settings are set to the fallback for the user settings. @see getCommonSettings */ PropertiesFile* getUserSettings(); /** Returns the common settings file. The first time this is called, it will create and load the properties file. @param returnUserPropsIfReadOnly if this is true, and the common properties file is read-only (e.g. because the user doesn't have permission to write to shared files), then this will return the user settings instead, (like getUserSettings() would do). This is handy if you'd like to write a value to the common settings, but if that's no possible, then you'd rather write to the user settings than none at all. If returnUserPropsIfReadOnly is false, this method will always return the common settings, even if any changes to them can't be saved. @see getUserSettings */ PropertiesFile* getCommonSettings (bool returnUserPropsIfReadOnly); //============================================================================== /** Saves both files if they need to be saved. @see PropertiesFile::saveIfNeeded */ bool saveIfNeeded(); /** Flushes and closes both files if they are open. This flushes any pending changes to disk with PropertiesFile::saveIfNeeded() and closes both files. They will then be re-opened the next time getUserSettings() or getCommonSettings() is called. */ void closeFiles(); private: //============================================================================== PropertiesFile::Options options; ScopedPointer userProps, commonProps; int commonSettingsAreReadOnly; void openFiles(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationProperties) }; #endif // JUCE_APPLICATIONPROPERTIES_H_INCLUDED juce_PropertiesFile.cpp000066400000000000000000000256301320201440200354570ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/app_properties/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace PropertyFileConstants { static const int magicNumber = (int) ByteOrder::littleEndianInt ("PROP"); static const int magicNumberCompressed = (int) ByteOrder::littleEndianInt ("CPRP"); static const char* const fileTag = "PROPERTIES"; static const char* const valueTag = "VALUE"; static const char* const nameAttribute = "name"; static const char* const valueAttribute = "val"; } //============================================================================== PropertiesFile::Options::Options() : commonToAllUsers (false), ignoreCaseOfKeyNames (false), doNotSave (false), millisecondsBeforeSaving (3000), storageFormat (PropertiesFile::storeAsXML), processLock (nullptr) { } File PropertiesFile::Options::getDefaultFile() const { // mustn't have illegal characters in this name.. jassert (applicationName == File::createLegalFileName (applicationName)); #if JUCE_MAC || JUCE_IOS File dir (commonToAllUsers ? "/Library/" : "~/Library/"); if (osxLibrarySubFolder != "Preferences" && ! osxLibrarySubFolder.startsWith ("Application Support")) { /* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple have changed their advice, and now stipulate that settings should go in "Library/Application Support". Because older apps would be broken by a silent change in this class's behaviour, you must now explicitly set the osxLibrarySubFolder value to indicate which path you want to use. In newer apps, you should always set this to "Application Support" or "Application Support/YourSubFolderName". If your app needs to load settings files that were created by older versions of juce and you want to maintain backwards-compatibility, then you can set this to "Preferences". But.. for better Apple-compliance, the recommended approach would be to write some code that finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support, and then uses the new path. */ jassertfalse; dir = dir.getChildFile ("Application Support"); } else { dir = dir.getChildFile (osxLibrarySubFolder); } if (folderName.isNotEmpty()) dir = dir.getChildFile (folderName); #elif JUCE_LINUX || JUCE_ANDROID const File dir (File (commonToAllUsers ? "/var" : "~") .getChildFile (folderName.isNotEmpty() ? folderName : ("." + applicationName))); #elif JUCE_WINDOWS File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory : File::userApplicationDataDirectory)); if (dir == File()) return File(); dir = dir.getChildFile (folderName.isNotEmpty() ? folderName : applicationName); #endif return dir.getChildFile (applicationName) .withFileExtension (filenameSuffix); } //============================================================================== PropertiesFile::PropertiesFile (const File& f, const Options& o) : PropertySet (o.ignoreCaseOfKeyNames), file (f), options (o), loadedOk (false), needsWriting (false) { reload(); } PropertiesFile::PropertiesFile (const Options& o) : PropertySet (o.ignoreCaseOfKeyNames), file (o.getDefaultFile()), options (o), loadedOk (false), needsWriting (false) { reload(); } bool PropertiesFile::reload() { ProcessScopedLock pl (createProcessLock()); if (pl != nullptr && ! pl->isLocked()) return false; // locking failure.. loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml(); return loadedOk; } PropertiesFile::~PropertiesFile() { saveIfNeeded(); } InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const { return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr; } bool PropertiesFile::saveIfNeeded() { const ScopedLock sl (getLock()); return (! needsWriting) || save(); } bool PropertiesFile::needsToBeSaved() const { const ScopedLock sl (getLock()); return needsWriting; } void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_) { const ScopedLock sl (getLock()); needsWriting = needsToBeSaved_; } bool PropertiesFile::save() { const ScopedLock sl (getLock()); stopTimer(); if (options.doNotSave || file == File() || file.isDirectory() || ! file.getParentDirectory().createDirectory()) return false; if (options.storageFormat == storeAsXML) return saveAsXml(); return saveAsBinary(); } bool PropertiesFile::loadAsXml() { XmlDocument parser (file); ScopedPointer doc (parser.getDocumentElement (true)); if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag)) { doc = parser.getDocumentElement(); if (doc != nullptr) { forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) { const String name (e->getStringAttribute (PropertyFileConstants::nameAttribute)); if (name.isNotEmpty()) { getAllProperties().set (name, e->getFirstChildElement() != nullptr ? e->getFirstChildElement()->createDocument ("", true) : e->getStringAttribute (PropertyFileConstants::valueAttribute)); } } return true; } // must be a pretty broken XML file we're trying to parse here, // or a sign that this object needs an InterProcessLock, // or just a failure reading the file. This last reason is why // we don't jassertfalse here. } return false; } bool PropertiesFile::saveAsXml() { XmlElement doc (PropertyFileConstants::fileTag); const StringPairArray& props = getAllProperties(); for (int i = 0; i < props.size(); ++i) { XmlElement* const e = doc.createNewChildElement (PropertyFileConstants::valueTag); e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]); // if the value seems to contain xml, store it as such.. if (XmlElement* const childElement = XmlDocument::parse (props.getAllValues() [i])) e->addChildElement (childElement); else e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]); } ProcessScopedLock pl (createProcessLock()); if (pl != nullptr && ! pl->isLocked()) return false; // locking failure.. if (doc.writeToFile (file, String())) { needsWriting = false; return true; } return false; } bool PropertiesFile::loadAsBinary() { FileInputStream fileStream (file); if (fileStream.openedOk()) { const int magicNumber = fileStream.readInt(); if (magicNumber == PropertyFileConstants::magicNumberCompressed) { SubregionStream subStream (&fileStream, 4, -1, false); GZIPDecompressorInputStream gzip (subStream); return loadAsBinary (gzip); } if (magicNumber == PropertyFileConstants::magicNumber) return loadAsBinary (fileStream); } return false; } bool PropertiesFile::loadAsBinary (InputStream& input) { BufferedInputStream in (input, 2048); int numValues = in.readInt(); while (--numValues >= 0 && ! in.isExhausted()) { const String key (in.readString()); const String value (in.readString()); jassert (key.isNotEmpty()); if (key.isNotEmpty()) getAllProperties().set (key, value); } return true; } bool PropertiesFile::saveAsBinary() { ProcessScopedLock pl (createProcessLock()); if (pl != nullptr && ! pl->isLocked()) return false; // locking failure.. TemporaryFile tempFile (file); ScopedPointer out (tempFile.getFile().createOutputStream()); if (out != nullptr) { if (options.storageFormat == storeAsCompressedBinary) { out->writeInt (PropertyFileConstants::magicNumberCompressed); out->flush(); out = new GZIPCompressorOutputStream (out.release(), 9, true); } else { // have you set up the storage option flags correctly? jassert (options.storageFormat == storeAsBinary); out->writeInt (PropertyFileConstants::magicNumber); } const StringPairArray& props = getAllProperties(); const int numProperties = props.size(); const StringArray& keys = props.getAllKeys(); const StringArray& values = props.getAllValues(); out->writeInt (numProperties); for (int i = 0; i < numProperties; ++i) { out->writeString (keys[i]); out->writeString (values[i]); } out = nullptr; if (tempFile.overwriteTargetFileWithTemporary()) { needsWriting = false; return true; } } return false; } void PropertiesFile::timerCallback() { saveIfNeeded(); } void PropertiesFile::propertyChanged() { sendChangeMessage(); needsWriting = true; if (options.millisecondsBeforeSaving > 0) startTimer (options.millisecondsBeforeSaving); else if (options.millisecondsBeforeSaving == 0) saveIfNeeded(); } juce_PropertiesFile.h000066400000000000000000000245231320201440200351240ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/app_properties/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PROPERTIESFILE_H_INCLUDED #define JUCE_PROPERTIESFILE_H_INCLUDED //============================================================================== /** Wrapper on a file that stores a list of key/value data pairs. Useful for storing application settings, etc. See the PropertySet class for the interfaces that read and write values. Not designed for very large amounts of data, as it keeps all the values in memory and writes them out to disk lazily when they are changed. Because this class derives from ChangeBroadcaster, ChangeListeners can be registered with it, and these will be signalled when a value changes. @see PropertySet */ class JUCE_API PropertiesFile : public PropertySet, public ChangeBroadcaster, private Timer { public: //============================================================================== enum StorageFormat { storeAsBinary, storeAsCompressedBinary, storeAsXML }; //============================================================================== struct JUCE_API Options { /** Creates an empty Options structure. You'll need to fill-in the data members appropriately before using this structure. */ Options(); /** The name of your application - this is used to help generate the path and filename at which the properties file will be stored. */ String applicationName; /** The suffix to use for your properties file. It doesn't really matter what this is - you may want to use ".settings" or ".properties" or something. */ String filenameSuffix; /** The name of a subfolder in which you'd like your properties file to live. See the getDefaultFile() method for more details about how this is used. */ String folderName; /** If you're using properties files on a Mac, you must set this value - failure to do so will cause a runtime assertion. The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple have changed their advice, and now stipulate that settings should go in "Library/Application Support". Because older apps would be broken by a silent change in this class's behaviour, you must now explicitly set the osxLibrarySubFolder value to indicate which path you want to use. In newer apps, you should always set this to "Application Support" or "Application Support/YourSubFolderName". If your app needs to load settings files that were created by older versions of juce and you want to maintain backwards-compatibility, then you can set this to "Preferences". But.. for better Apple-compliance, the recommended approach would be to write some code that finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support, and then uses the new path. */ String osxLibrarySubFolder; /** If true, the file will be created in a location that's shared between users. The default constructor initialises this value to false. */ bool commonToAllUsers; /** If true, this means that property names are matched in a case-insensitive manner. See the PropertySet constructor for more info. The default constructor initialises this value to false. */ bool ignoreCaseOfKeyNames; /** If set to true, this prevents the file from being written to disk. */ bool doNotSave; /** If this is zero or greater, then after a value is changed, the object will wait for this amount of time and then save the file. If this zero, the file will be written to disk immediately on being changed (which might be slow, as it'll re-write synchronously each time a value-change method is called). If it is less than zero, the file won't be saved until save() or saveIfNeeded() are explicitly called. The default constructor sets this to a reasonable value of a few seconds, so you only need to change it if you need a special case. */ int millisecondsBeforeSaving; /** Specifies whether the file should be written as XML, binary, etc. The default constructor sets this to storeAsXML, so you only need to set it explicitly if you want to use a different format. */ StorageFormat storageFormat; /** An optional InterprocessLock object that will be used to prevent multiple threads or processes from writing to the file at the same time. The PropertiesFile will keep a pointer to this object but will not take ownership of it - the caller is responsible for making sure that the lock doesn't get deleted before the PropertiesFile has been deleted. The default constructor initialises this value to nullptr, so you don't need to touch it unless you want to use a lock. */ InterProcessLock* processLock; /** This can be called to suggest a file that should be used, based on the values in this structure. So on a Mac, this will return a file called: ~/Library/[osxLibrarySubFolder]/[folderName]/[applicationName].[filenameSuffix] On Windows it'll return something like: C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[filenameSuffix] On Linux it'll return ~/[folderName]/[applicationName].[filenameSuffix] If the folderName variable is empty, it'll use the app name for this (or omit the folder name on the Mac). The paths will also vary depending on whether commonToAllUsers is true. */ File getDefaultFile() const; }; //============================================================================== /** Creates a PropertiesFile object. The file used will be chosen by calling PropertiesFile::Options::getDefaultFile() for the options provided. To set the file explicitly, use the other constructor. */ explicit PropertiesFile (const Options& options); /** Creates a PropertiesFile object. Unlike the other constructor, this one allows you to explicitly set the file that you want to be used, rather than using the default one. */ PropertiesFile (const File& file, const Options& options); /** Destructor. When deleted, the file will first call saveIfNeeded() to flush any changes to disk. */ ~PropertiesFile(); //============================================================================== /** Returns true if this file was created from a valid (or non-existent) file. If the file failed to load correctly because it was corrupt or had insufficient access, this will be false. */ bool isValidFile() const noexcept { return loadedOk; } //============================================================================== /** This will flush all the values to disk if they've changed since the last time they were saved. Returns false if it fails to write to the file for some reason (maybe because it's read-only or the directory doesn't exist or something). @see save */ bool saveIfNeeded(); /** This will force a write-to-disk of the current values, regardless of whether anything has changed since the last save. Returns false if it fails to write to the file for some reason (maybe because it's read-only or the directory doesn't exist or something). @see saveIfNeeded */ bool save(); /** Returns true if the properties have been altered since the last time they were saved. The file is flagged as needing to be saved when you change a value, but you can explicitly set this flag with setNeedsToBeSaved(). */ bool needsToBeSaved() const; /** Explicitly sets the flag to indicate whether the file needs saving or not. @see needsToBeSaved */ void setNeedsToBeSaved (bool needsToBeSaved); /** Attempts to reload the settings from the file. */ bool reload(); //============================================================================== /** Returns the file that's being used. */ const File& getFile() const noexcept { return file; } protected: /** @internal */ void propertyChanged() override; private: //============================================================================== File file; Options options; bool loadedOk, needsWriting; typedef const ScopedPointer ProcessScopedLock; InterProcessLock::ScopedLockType* createProcessLock() const; void timerCallback() override; bool saveAsXml(); bool saveAsBinary(); bool loadAsXml(); bool loadAsBinary(); bool loadAsBinary (InputStream&); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile) }; #endif // JUCE_PROPERTIESFILE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp000066400000000000000000000036501320201440200327600ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if defined (JUCE_DATA_STRUCTURES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" #include "juce_data_structures.h" namespace juce { #include "values/juce_Value.cpp" #include "values/juce_ValueTree.cpp" #include "values/juce_ValueTreeSynchroniser.cpp" #include "undomanager/juce_UndoManager.cpp" #include "app_properties/juce_ApplicationProperties.cpp" #include "app_properties/juce_PropertiesFile.cpp" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h000066400000000000000000000027621320201440200324300ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DATA_STRUCTURES_H_INCLUDED #define JUCE_DATA_STRUCTURES_H_INCLUDED //============================================================================= #include "../juce_events/juce_events.h" namespace juce { #include "undomanager/juce_UndoableAction.h" #include "undomanager/juce_UndoManager.h" #include "values/juce_Value.h" #include "values/juce_ValueTree.h" #include "values/juce_ValueTreeSynchroniser.h" #include "app_properties/juce_PropertiesFile.h" #include "app_properties/juce_ApplicationProperties.h" } #endif // JUCE_DATA_STRUCTURES_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm000066400000000000000000000017101320201440200326020ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_data_structures.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/juce_module_info000066400000000000000000000014331320201440200312600ustar00rootroot00000000000000{ "id": "juce_data_structures", "name": "JUCE data model helper classes", "version": "3.2.0", "description": "Classes for undo/redo management, and smart data structures.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", "dependencies": [ { "id": "juce_core", "version": "matching" }, { "id": "juce_events", "version": "matching" } ], "include": "juce_data_structures.h", "compile": [ { "file": "juce_data_structures.cpp", "target": "! xcode" }, { "file": "juce_data_structures.mm", "target": "xcode" } ], "browse": [ "values/*", "undomanager/*", "app_properties/*" ] } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/undomanager/000077500000000000000000000000001320201440200303265ustar00rootroot00000000000000juce_UndoManager.cpp000066400000000000000000000207341320201440200341670ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/undomanager/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct UndoManager::ActionSet { ActionSet (const String& transactionName) : name (transactionName), time (Time::getCurrentTime()) {} bool perform() const { for (int i = 0; i < actions.size(); ++i) if (! actions.getUnchecked(i)->perform()) return false; return true; } bool undo() const { for (int i = actions.size(); --i >= 0;) if (! actions.getUnchecked(i)->undo()) return false; return true; } int getTotalSize() const { int total = 0; for (int i = actions.size(); --i >= 0;) total += actions.getUnchecked(i)->getSizeInUnits(); return total; } OwnedArray actions; String name; Time time; }; //============================================================================== UndoManager::UndoManager (const int maxNumberOfUnitsToKeep, const int minimumTransactions) : totalUnitsStored (0), nextIndex (0), newTransaction (true), reentrancyCheck (false) { setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep, minimumTransactions); } UndoManager::~UndoManager() { } //============================================================================== void UndoManager::clearUndoHistory() { transactions.clear(); totalUnitsStored = 0; nextIndex = 0; sendChangeMessage(); } int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const { return totalUnitsStored; } void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep, const int minimumTransactions) { maxNumUnitsToKeep = jmax (1, maxNumberOfUnitsToKeep); minimumTransactionsToKeep = jmax (1, minimumTransactions); } //============================================================================== bool UndoManager::perform (UndoableAction* const newAction, const String& actionName) { if (perform (newAction)) { if (actionName.isNotEmpty()) setCurrentTransactionName (actionName); return true; } return false; } bool UndoManager::perform (UndoableAction* const newAction) { if (newAction != nullptr) { ScopedPointer action (newAction); if (reentrancyCheck) { jassertfalse; // don't call perform() recursively from the UndoableAction::perform() // or undo() methods, or else these actions will be discarded! return false; } if (action->perform()) { ActionSet* actionSet = getCurrentSet(); if (actionSet != nullptr && ! newTransaction) { if (UndoableAction* const lastAction = actionSet->actions.getLast()) { if (UndoableAction* const coalescedAction = lastAction->createCoalescedAction (action)) { action = coalescedAction; totalUnitsStored -= lastAction->getSizeInUnits(); actionSet->actions.removeLast(); } } } else { actionSet = new ActionSet (newTransactionName); transactions.insert (nextIndex, actionSet); ++nextIndex; } totalUnitsStored += action->getSizeInUnits(); actionSet->actions.add (action.release()); newTransaction = false; clearFutureTransactions(); sendChangeMessage(); return true; } } return false; } void UndoManager::clearFutureTransactions() { while (nextIndex < transactions.size()) { totalUnitsStored -= transactions.getLast()->getTotalSize(); transactions.removeLast(); } while (nextIndex > 0 && totalUnitsStored > maxNumUnitsToKeep && transactions.size() > minimumTransactionsToKeep) { totalUnitsStored -= transactions.getFirst()->getTotalSize(); transactions.remove (0); --nextIndex; // if this fails, then some actions may not be returning // consistent results from their getSizeInUnits() method jassert (totalUnitsStored >= 0); } } void UndoManager::beginNewTransaction() noexcept { beginNewTransaction (String()); } void UndoManager::beginNewTransaction (const String& actionName) noexcept { newTransaction = true; newTransactionName = actionName; } void UndoManager::setCurrentTransactionName (const String& newName) noexcept { if (newTransaction) newTransactionName = newName; else if (ActionSet* action = getCurrentSet()) action->name = newName; } String UndoManager::getCurrentTransactionName() const noexcept { if (ActionSet* action = getCurrentSet()) return action->name; return newTransactionName; } //============================================================================== UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; } UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; } bool UndoManager::canUndo() const noexcept { return getCurrentSet() != nullptr; } bool UndoManager::canRedo() const noexcept { return getNextSet() != nullptr; } bool UndoManager::undo() { if (const ActionSet* const s = getCurrentSet()) { const ScopedValueSetter setter (reentrancyCheck, true); if (s->undo()) --nextIndex; else clearUndoHistory(); beginNewTransaction(); sendChangeMessage(); return true; } return false; } bool UndoManager::redo() { if (const ActionSet* const s = getNextSet()) { const ScopedValueSetter setter (reentrancyCheck, true); if (s->perform()) ++nextIndex; else clearUndoHistory(); beginNewTransaction(); sendChangeMessage(); return true; } return false; } String UndoManager::getUndoDescription() const { if (const ActionSet* const s = getCurrentSet()) return s->name; return String(); } String UndoManager::getRedoDescription() const { if (const ActionSet* const s = getNextSet()) return s->name; return String(); } Time UndoManager::getTimeOfUndoTransaction() const { if (const ActionSet* const s = getCurrentSet()) return s->time; return Time(); } Time UndoManager::getTimeOfRedoTransaction() const { if (const ActionSet* const s = getNextSet()) return s->time; return Time::getCurrentTime(); } bool UndoManager::undoCurrentTransactionOnly() { return newTransaction ? false : undo(); } void UndoManager::getActionsInCurrentTransaction (Array& actionsFound) const { if (! newTransaction) if (const ActionSet* const s = getCurrentSet()) for (int i = 0; i < s->actions.size(); ++i) actionsFound.add (s->actions.getUnchecked(i)); } int UndoManager::getNumActionsInCurrentTransaction() const { if (! newTransaction) if (const ActionSet* const s = getCurrentSet()) return s->actions.size(); return 0; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h000066400000000000000000000251521320201440200337120ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_UNDOMANAGER_H_INCLUDED #define JUCE_UNDOMANAGER_H_INCLUDED //============================================================================== /** Manages a list of undo/redo commands. An UndoManager object keeps a list of past actions and can use these actions to move backwards and forwards through an undo history. To use it, create subclasses of UndoableAction which perform all the actions you need, then when you need to actually perform an action, create one and pass it to the UndoManager's perform() method. The manager also uses the concept of 'transactions' to group the actions together - all actions performed between calls to beginNewTransaction() are grouped together and are all undone/redone as a group. The UndoManager is a ChangeBroadcaster, so listeners can register to be told when actions are performed or undone. @see UndoableAction */ class JUCE_API UndoManager : public ChangeBroadcaster { public: //============================================================================== /** Creates an UndoManager. @param maxNumberOfUnitsToKeep each UndoableAction object returns a value to indicate how much storage it takes up (UndoableAction::getSizeInUnits()), so this lets you specify the maximum total number of units that the undomanager is allowed to keep in memory before letting the older actions drop off the end of the list. @param minimumTransactionsToKeep this specifies the minimum number of transactions that will be kept, even if this involves exceeding the amount of space specified in maxNumberOfUnitsToKeep */ UndoManager (int maxNumberOfUnitsToKeep = 30000, int minimumTransactionsToKeep = 30); /** Destructor. */ ~UndoManager(); //============================================================================== /** Deletes all stored actions in the list. */ void clearUndoHistory(); /** Returns the current amount of space to use for storing UndoableAction objects. @see setMaxNumberOfStoredUnits */ int getNumberOfUnitsTakenUpByStoredCommands() const; /** Sets the amount of space that can be used for storing UndoableAction objects. @param maxNumberOfUnitsToKeep each UndoableAction object returns a value to indicate how much storage it takes up (UndoableAction::getSizeInUnits()), so this lets you specify the maximum total number of units that the undomanager is allowed to keep in memory before letting the older actions drop off the end of the list. @param minimumTransactionsToKeep this specifies the minimum number of transactions that will be kept, even if this involves exceeding the amount of space specified in maxNumberOfUnitsToKeep @see getNumberOfUnitsTakenUpByStoredCommands */ void setMaxNumberOfStoredUnits (int maxNumberOfUnitsToKeep, int minimumTransactionsToKeep); //============================================================================== /** Performs an action and adds it to the undo history list. @param action the action to perform - this object will be deleted by the UndoManager when no longer needed @returns true if the command succeeds - see UndoableAction::perform @see beginNewTransaction */ bool perform (UndoableAction* action); /** Performs an action and also gives it a name. @param action the action to perform - this object will be deleted by the UndoManager when no longer needed @param actionName if this string is non-empty, the current transaction will be given this name; if it's empty, the current transaction name will be left unchanged. See setCurrentTransactionName() @returns true if the command succeeds - see UndoableAction::perform @see beginNewTransaction */ bool perform (UndoableAction* action, const String& actionName); /** Starts a new group of actions that together will be treated as a single transaction. All actions that are passed to the perform() method between calls to this method are grouped together and undone/redone together by a single call to undo() or redo(). */ void beginNewTransaction() noexcept; /** Starts a new group of actions that together will be treated as a single transaction. All actions that are passed to the perform() method between calls to this method are grouped together and undone/redone together by a single call to undo() or redo(). @param actionName a description of the transaction that is about to be performed */ void beginNewTransaction (const String& actionName) noexcept; /** Changes the name stored for the current transaction. Each transaction is given a name when the beginNewTransaction() method is called, but this can be used to change that name without starting a new transaction. */ void setCurrentTransactionName (const String& newName) noexcept; /** Returns the name of the current transaction. @see setCurrentTransactionName */ String getCurrentTransactionName() const noexcept; //============================================================================== /** Returns true if there's at least one action in the list to undo. @see getUndoDescription, undo, canRedo */ bool canUndo() const noexcept; /** Returns the name of the transaction that will be rolled-back when undo() is called. @see undo */ String getUndoDescription() const; /** Tries to roll-back the last transaction. @returns true if the transaction can be undone, and false if it fails, or if there aren't any transactions to undo */ bool undo(); /** Tries to roll-back any actions that were added to the current transaction. This will perform an undo() only if there are some actions in the undo list that were added after the last call to beginNewTransaction(). This is useful because it lets you call beginNewTransaction(), then perform an operation which may or may not actually perform some actions, and then call this method to get rid of any actions that might have been done without it rolling back the previous transaction if nothing was actually done. @returns true if any actions were undone. */ bool undoCurrentTransactionOnly(); /** Returns a list of the UndoableAction objects that have been performed during the transaction that is currently open. Effectively, this is the list of actions that would be undone if undoCurrentTransactionOnly() were to be called now. The first item in the list is the earliest action performed. */ void getActionsInCurrentTransaction (Array& actionsFound) const; /** Returns the number of UndoableAction objects that have been performed during the transaction that is currently open. @see getActionsInCurrentTransaction */ int getNumActionsInCurrentTransaction() const; /** Returns the time to which the state would be restored if undo() was to be called. If an undo isn't currently possible, it'll return Time(). */ Time getTimeOfUndoTransaction() const; /** Returns the time to which the state would be restored if redo() was to be called. If a redo isn't currently possible, it'll return Time::getCurrentTime(). */ Time getTimeOfRedoTransaction() const; //============================================================================== /** Returns true if there's at least one action in the list to redo. @see getRedoDescription, redo, canUndo */ bool canRedo() const noexcept; /** Returns the name of the transaction that will be redone when redo() is called. @see redo */ String getRedoDescription() const; /** Tries to redo the last transaction that was undone. @returns true if the transaction can be redone, and false if it fails, or if there aren't any transactions to redo */ bool redo(); private: //============================================================================== struct ActionSet; friend struct ContainerDeletePolicy; OwnedArray transactions; String newTransactionName; int totalUnitsStored, maxNumUnitsToKeep, minimumTransactionsToKeep, nextIndex; bool newTransaction, reentrancyCheck; ActionSet* getCurrentSet() const noexcept; ActionSet* getNextSet() const noexcept; void clearFutureTransactions(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UndoManager) }; #endif // JUCE_UNDOMANAGER_H_INCLUDED juce_UndoableAction.h000066400000000000000000000070131320201440200343160ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/undomanager/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_UNDOABLEACTION_H_INCLUDED #define JUCE_UNDOABLEACTION_H_INCLUDED //============================================================================== /** Used by the UndoManager class to store an action which can be done and undone. @see UndoManager */ class JUCE_API UndoableAction { protected: /** Creates an action. */ UndoableAction() noexcept {} public: /** Destructor. */ virtual ~UndoableAction() {} //============================================================================== /** Overridden by a subclass to perform the action. This method is called by the UndoManager, and shouldn't be used directly by applications. Be careful not to make any calls in a perform() method that could call recursively back into the UndoManager::perform() method @returns true if the action could be performed. @see UndoManager::perform */ virtual bool perform() = 0; /** Overridden by a subclass to undo the action. This method is called by the UndoManager, and shouldn't be used directly by applications. Be careful not to make any calls in an undo() method that could call recursively back into the UndoManager::perform() method @returns true if the action could be undone without any errors. @see UndoManager::perform */ virtual bool undo() = 0; //============================================================================== /** Returns a value to indicate how much memory this object takes up. Because the UndoManager keeps a list of UndoableActions, this is used to work out how much space each one will take up, so that the UndoManager can work out how many to keep. The default value returned here is 10 - units are arbitrary and don't have to be accurate. @see UndoManager::getNumberOfUnitsTakenUpByStoredCommands, UndoManager::setMaxNumberOfStoredUnits */ virtual int getSizeInUnits() { return 10; } /** Allows multiple actions to be coalesced into a single action object, to reduce storage space. If possible, this method should create and return a single action that does the same job as this one followed by the supplied action. If it's not possible to merge the two actions, the method should return zero. */ virtual UndoableAction* createCoalescedAction (UndoableAction* nextAction) { (void) nextAction; return nullptr; } }; #endif // JUCE_UNDOABLEACTION_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/values/000077500000000000000000000000001320201440200273255ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp000066400000000000000000000133331320201440200321160ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ Value::ValueSource::ValueSource() { } Value::ValueSource::~ValueSource() { cancelPendingUpdate(); } void Value::ValueSource::handleAsyncUpdate() { sendChangeMessage (true); } void Value::ValueSource::sendChangeMessage (const bool synchronous) { const int numListeners = valuesWithListeners.size(); if (numListeners > 0) { if (synchronous) { const ReferenceCountedObjectPtr localRef (this); cancelPendingUpdate(); for (int i = numListeners; --i >= 0;) if (Value* const v = valuesWithListeners[i]) v->callListeners(); } else { triggerAsyncUpdate(); } } } //============================================================================== class SimpleValueSource : public Value::ValueSource { public: SimpleValueSource() { } SimpleValueSource (const var& initialValue) : value (initialValue) { } var getValue() const override { return value; } void setValue (const var& newValue) override { if (! newValue.equalsWithSameType (value)) { value = newValue; sendChangeMessage (false); } } private: var value; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleValueSource) }; //============================================================================== Value::Value() : value (new SimpleValueSource()) { } Value::Value (ValueSource* const v) : value (v) { jassert (v != nullptr); } Value::Value (const var& initialValue) : value (new SimpleValueSource (initialValue)) { } Value::Value (const Value& other) : value (other.value) { } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Value::Value (Value&& other) noexcept { // moving a Value with listeners will lose those listeners, which // probably isn't what you wanted to happen! jassert (other.listeners.size() == 0); other.removeFromListenerList(); value = static_cast&&> (other.value); } Value& Value::operator= (Value&& other) noexcept { // moving a Value with listeners will lose those listeners, which // probably isn't what you wanted to happen! jassert (other.listeners.size() == 0); other.removeFromListenerList(); value = static_cast&&> (other.value); return *this; } #endif Value::~Value() { removeFromListenerList(); } void Value::removeFromListenerList() { if (listeners.size() > 0 && value != nullptr) // may be nullptr after a move operation value->valuesWithListeners.removeValue (this); } //============================================================================== var Value::getValue() const { return value->getValue(); } Value::operator var() const { return value->getValue(); } void Value::setValue (const var& newValue) { value->setValue (newValue); } String Value::toString() const { return value->getValue().toString(); } Value& Value::operator= (const var& newValue) { value->setValue (newValue); return *this; } void Value::referTo (const Value& valueToReferTo) { if (valueToReferTo.value != value) { if (listeners.size() > 0) { value->valuesWithListeners.removeValue (this); valueToReferTo.value->valuesWithListeners.add (this); } value = valueToReferTo.value; callListeners(); } } bool Value::refersToSameSourceAs (const Value& other) const { return value == other.value; } bool Value::operator== (const Value& other) const { return value == other.value || value->getValue() == other.getValue(); } bool Value::operator!= (const Value& other) const { return value != other.value && value->getValue() != other.getValue(); } //============================================================================== void Value::addListener (ValueListener* const listener) { if (listener != nullptr) { if (listeners.size() == 0) value->valuesWithListeners.add (this); listeners.add (listener); } } void Value::removeListener (ValueListener* const listener) { listeners.remove (listener); if (listeners.size() == 0) value->valuesWithListeners.removeValue (this); } void Value::callListeners() { if (listeners.size() > 0) { Value v (*this); // (create a copy in case this gets deleted by a callback) listeners.call (&ValueListener::valueChanged, v); } } OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const Value& value) { return stream << value.toString(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h000066400000000000000000000215601320201440200315640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_VALUE_H_INCLUDED #define JUCE_VALUE_H_INCLUDED //============================================================================== /** Represents a shared variant value. A Value object contains a reference to a var object, and can get and set its value. Listeners can be attached to be told when the value is changed. The Value class is a wrapper around a shared, reference-counted underlying data object - this means that multiple Value objects can all refer to the same piece of data, allowing all of them to be notified when any of them changes it. When you create a Value with its default constructor, it acts as a wrapper around a simple var object, but by creating a Value that refers to a custom subclass of ValueSource, you can map the Value onto any kind of underlying data. */ class JUCE_API Value { public: //============================================================================== /** Creates an empty Value, containing a void var. */ Value(); /** Creates a Value that refers to the same value as another one. Note that this doesn't make a copy of the other value - both this and the other Value will share the same underlying value, so that when either one alters it, both will see it change. */ Value (const Value& other); /** Creates a Value that is set to the specified value. */ explicit Value (const var& initialValue); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Value (Value&&) noexcept; Value& operator= (Value&&) noexcept; #endif /** Destructor. */ ~Value(); //============================================================================== /** Returns the current value. */ var getValue() const; /** Returns the current value. */ operator var() const; /** Returns the value as a string. This is a shortcut for "myValue.getValue().toString()". */ String toString() const; /** Sets the current value. You can also use operator= to set the value. If there are any listeners registered, they will be notified of the change asynchronously. */ void setValue (const var& newValue); /** Sets the current value. This is the same as calling setValue(). If there are any listeners registered, they will be notified of the change asynchronously. */ Value& operator= (const var& newValue); /** Makes this object refer to the same underlying ValueSource as another one. Once this object has been connected to another one, changing either one will update the other. Existing listeners will still be registered after you call this method, and they'll continue to receive messages when the new value changes. */ void referTo (const Value& valueToReferTo); /** Returns true if this value and the other one are references to the same value. */ bool refersToSameSourceAs (const Value& other) const; /** Compares two values. This is a compare-by-value comparison, so is effectively the same as saying (this->getValue() == other.getValue()). */ bool operator== (const Value& other) const; /** Compares two values. This is a compare-by-value comparison, so is effectively the same as saying (this->getValue() != other.getValue()). */ bool operator!= (const Value& other) const; //============================================================================== /** Receives callbacks when a Value object changes. @see Value::addListener */ class JUCE_API Listener { public: Listener() {} virtual ~Listener() {} /** Called when a Value object is changed. Note that the Value object passed as a parameter may not be exactly the same object that you registered the listener with - it might be a copy that refers to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs(). */ virtual void valueChanged (Value& value) = 0; }; /** Adds a listener to receive callbacks when the value changes. The listener is added to this specific Value object, and not to the shared object that it refers to. When this object is deleted, all the listeners will be lost, even if other references to the same Value still exist. So when you're adding a listener, make sure that you add it to a Value instance that will last for as long as you need the listener. In general, you'd never want to add a listener to a local stack-based Value, but more likely to one that's a member variable. @see removeListener */ void addListener (Listener* listener); /** Removes a listener that was previously added with addListener(). */ void removeListener (Listener* listener); //============================================================================== /** Used internally by the Value class as the base class for its shared value objects. The Value class is essentially a reference-counted pointer to a shared instance of a ValueSource object. If you're feeling adventurous, you can create your own custom ValueSource classes to allow Value objects to represent your own custom data items. */ class JUCE_API ValueSource : public ReferenceCountedObject, private AsyncUpdater { public: ValueSource(); virtual ~ValueSource(); /** Returns the current value of this object. */ virtual var getValue() const = 0; /** Changes the current value. This must also trigger a change message if the value actually changes. */ virtual void setValue (const var& newValue) = 0; /** Delivers a change message to all the listeners that are registered with this value. If dispatchSynchronously is true, the method will call all the listeners before returning; otherwise it'll dispatch a message and make the call later. */ void sendChangeMessage (bool dispatchSynchronously); protected: //============================================================================== friend class Value; SortedSet valuesWithListeners; private: void handleAsyncUpdate() override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource) }; //============================================================================== /** Creates a Value object that uses this valueSource object as its underlying data. */ explicit Value (ValueSource* valueSource); /** Returns the ValueSource that this value is referring to. */ ValueSource& getValueSource() noexcept { return *value; } private: //============================================================================== friend class ValueSource; ReferenceCountedObjectPtr value; ListenerList listeners; void callListeners(); void removeFromListenerList(); // This is disallowed to avoid confusion about whether it should // do a by-value or by-reference copy. Value& operator= (const Value&) JUCE_DELETED_FUNCTION; // This declaration prevents accidental construction from an integer of 0, // which is possible in some compilers via an implicit cast to a pointer. explicit Value (void*) JUCE_DELETED_FUNCTION; }; /** Writes a Value to an OutputStream as a UTF8 string. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream&, const Value&); /** This typedef is just for compatibility with old code - newer code should use the Value::Listener class directly. */ typedef Value::Listener ValueListener; #endif // JUCE_VALUE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp000066400000000000000000001075111320201440200327400ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class ValueTree::SharedObject : public ReferenceCountedObject { public: typedef ReferenceCountedObjectPtr Ptr; explicit SharedObject (const Identifier& t) noexcept : type (t), parent (nullptr) { } SharedObject (const SharedObject& other) : ReferenceCountedObject(), type (other.type), properties (other.properties), parent (nullptr) { for (int i = 0; i < other.children.size(); ++i) { SharedObject* const child = new SharedObject (*other.children.getObjectPointerUnchecked(i)); child->parent = this; children.add (child); } } ~SharedObject() { jassert (parent == nullptr); // this should never happen unless something isn't obeying the ref-counting! for (int i = children.size(); --i >= 0;) { const Ptr c (children.getObjectPointerUnchecked(i)); c->parent = nullptr; children.remove (i); c->sendParentChangeMessage(); } } template void callListeners (Method method, ValueTree& tree) const { const int numListeners = valueTreesWithListeners.size(); if (numListeners == 1) { valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree); } else if (numListeners > 0) { const SortedSet listenersCopy (valueTreesWithListeners); for (int i = 0; i < numListeners; ++i) { ValueTree* const v = listenersCopy.getUnchecked(i); if (i == 0 || valueTreesWithListeners.contains (v)) v->listeners.call (method, tree); } } } template void callListeners (Method method, ValueTree& tree, ParamType& param2) const { const int numListeners = valueTreesWithListeners.size(); if (numListeners == 1) { valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2); } else if (numListeners > 0) { const SortedSet listenersCopy (valueTreesWithListeners); for (int i = 0; i < numListeners; ++i) { ValueTree* const v = listenersCopy.getUnchecked(i); if (i == 0 || valueTreesWithListeners.contains (v)) v->listeners.call (method, tree, param2); } } } template void callListeners (Method method, ValueTree& tree, ParamType1& param2, ParamType2& param3) const { const int numListeners = valueTreesWithListeners.size(); if (numListeners == 1) { valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2, param3); } else if (numListeners > 0) { const SortedSet listenersCopy (valueTreesWithListeners); for (int i = 0; i < numListeners; ++i) { ValueTree* const v = listenersCopy.getUnchecked(i); if (i == 0 || valueTreesWithListeners.contains (v)) v->listeners.call (method, tree, param2, param3); } } } void sendPropertyChangeMessage (const Identifier& property) { ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) t->callListeners (&ValueTree::Listener::valueTreePropertyChanged, tree, property); } void sendChildAddedMessage (ValueTree child) { ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) t->callListeners (&ValueTree::Listener::valueTreeChildAdded, tree, child); } void sendChildRemovedMessage (ValueTree child, int index) { ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) t->callListeners (&ValueTree::Listener::valueTreeChildRemoved, tree, child, index); } void sendChildOrderChangedMessage (int oldIndex, int newIndex) { ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) t->callListeners (&ValueTree::Listener::valueTreeChildOrderChanged, tree, oldIndex, newIndex); } void sendParentChangeMessage() { ValueTree tree (this); for (int j = children.size(); --j >= 0;) if (SharedObject* const child = children.getObjectPointer (j)) child->sendParentChangeMessage(); callListeners (&ValueTree::Listener::valueTreeParentChanged, tree); } void setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager) { if (undoManager == nullptr) { if (properties.set (name, newValue)) sendPropertyChangeMessage (name); } else { if (const var* const existingValue = properties.getVarPointer (name)) { if (*existingValue != newValue) undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false)); } else { undoManager->perform (new SetPropertyAction (this, name, newValue, var(), true, false)); } } } bool hasProperty (const Identifier& name) const noexcept { return properties.contains (name); } void removeProperty (const Identifier& name, UndoManager* const undoManager) { if (undoManager == nullptr) { if (properties.remove (name)) sendPropertyChangeMessage (name); } else { if (properties.contains (name)) undoManager->perform (new SetPropertyAction (this, name, var(), properties [name], false, true)); } } void removeAllProperties (UndoManager* const undoManager) { if (undoManager == nullptr) { while (properties.size() > 0) { const Identifier name (properties.getName (properties.size() - 1)); properties.remove (name); sendPropertyChangeMessage (name); } } else { for (int i = properties.size(); --i >= 0;) undoManager->perform (new SetPropertyAction (this, properties.getName(i), var(), properties.getValueAt(i), false, true)); } } void copyPropertiesFrom (const SharedObject& source, UndoManager* const undoManager) { for (int i = properties.size(); --i >= 0;) if (! source.properties.contains (properties.getName (i))) removeProperty (properties.getName (i), undoManager); for (int i = 0; i < source.properties.size(); ++i) setProperty (source.properties.getName(i), source.properties.getValueAt(i), undoManager); } ValueTree getChildWithName (const Identifier& typeToMatch) const { for (int i = 0; i < children.size(); ++i) { SharedObject* const s = children.getObjectPointerUnchecked (i); if (s->type == typeToMatch) return ValueTree (s); } return ValueTree(); } ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager) { for (int i = 0; i < children.size(); ++i) { SharedObject* const s = children.getObjectPointerUnchecked (i); if (s->type == typeToMatch) return ValueTree (s); } SharedObject* const newObject = new SharedObject (typeToMatch); addChild (newObject, -1, undoManager); return ValueTree (newObject); } ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const { for (int i = 0; i < children.size(); ++i) { SharedObject* const s = children.getObjectPointerUnchecked (i); if (s->properties[propertyName] == propertyValue) return ValueTree (s); } return ValueTree(); } bool isAChildOf (const SharedObject* const possibleParent) const noexcept { for (const SharedObject* p = parent; p != nullptr; p = p->parent) if (p == possibleParent) return true; return false; } int indexOf (const ValueTree& child) const noexcept { return children.indexOf (child.object); } void addChild (SharedObject* child, int index, UndoManager* const undoManager) { if (child != nullptr && child->parent != this) { if (child != this && ! isAChildOf (child)) { // You should always make sure that a child is removed from its previous parent before // adding it somewhere else - otherwise, it's ambiguous as to whether a different // undomanager should be used when removing it from its current parent.. jassert (child->parent == nullptr); if (child->parent != nullptr) { jassert (child->parent->children.indexOf (child) >= 0); child->parent->removeChild (child->parent->children.indexOf (child), undoManager); } if (undoManager == nullptr) { children.insert (index, child); child->parent = this; sendChildAddedMessage (ValueTree (child)); child->sendParentChangeMessage(); } else { if (! isPositiveAndBelow (index, children.size())) index = children.size(); undoManager->perform (new AddOrRemoveChildAction (this, index, child)); } } else { // You're attempting to create a recursive loop! A node // can't be a child of one of its own children! jassertfalse; } } } void removeChild (const int childIndex, UndoManager* const undoManager) { if (const Ptr child = children.getObjectPointer (childIndex)) { if (undoManager == nullptr) { children.remove (childIndex); child->parent = nullptr; sendChildRemovedMessage (ValueTree (child), childIndex); child->sendParentChangeMessage(); } else { undoManager->perform (new AddOrRemoveChildAction (this, childIndex, nullptr)); } } } void removeAllChildren (UndoManager* const undoManager) { while (children.size() > 0) removeChild (children.size() - 1, undoManager); } void moveChild (int currentIndex, int newIndex, UndoManager* undoManager) { // The source index must be a valid index! jassert (isPositiveAndBelow (currentIndex, children.size())); if (currentIndex != newIndex && isPositiveAndBelow (currentIndex, children.size())) { if (undoManager == nullptr) { children.move (currentIndex, newIndex); sendChildOrderChangedMessage (currentIndex, newIndex); } else { if (! isPositiveAndBelow (newIndex, children.size())) newIndex = children.size() - 1; undoManager->perform (new MoveChildAction (this, currentIndex, newIndex)); } } } void reorderChildren (const OwnedArray& newOrder, UndoManager* undoManager) { jassert (newOrder.size() == children.size()); for (int i = 0; i < children.size(); ++i) { SharedObject* const child = newOrder.getUnchecked(i)->object; if (children.getObjectPointerUnchecked (i) != child) { const int oldIndex = children.indexOf (child); jassert (oldIndex >= 0); moveChild (oldIndex, i, undoManager); } } } bool isEquivalentTo (const SharedObject& other) const { if (type != other.type || properties.size() != other.properties.size() || children.size() != other.children.size() || properties != other.properties) return false; for (int i = 0; i < children.size(); ++i) if (! children.getObjectPointerUnchecked(i)->isEquivalentTo (*other.children.getObjectPointerUnchecked(i))) return false; return true; } XmlElement* createXml() const { XmlElement* const xml = new XmlElement (type); properties.copyToXmlAttributes (*xml); // (NB: it's faster to add nodes to XML elements in reverse order) for (int i = children.size(); --i >= 0;) xml->prependChildElement (children.getObjectPointerUnchecked(i)->createXml()); return xml; } void writeToStream (OutputStream& output) const { output.writeString (type.toString()); output.writeCompressedInt (properties.size()); for (int j = 0; j < properties.size(); ++j) { output.writeString (properties.getName (j).toString()); properties.getValueAt(j).writeToStream (output); } output.writeCompressedInt (children.size()); for (int i = 0; i < children.size(); ++i) writeObjectToStream (output, children.getObjectPointerUnchecked(i)); } static void writeObjectToStream (OutputStream& output, const SharedObject* const object) { if (object != nullptr) { object->writeToStream (output); } else { output.writeString (String()); output.writeCompressedInt (0); output.writeCompressedInt (0); } } //============================================================================== class SetPropertyAction : public UndoableAction { public: SetPropertyAction (SharedObject* const so, const Identifier& propertyName, const var& newVal, const var& oldVal, bool isAdding, bool isDeleting) : target (so), name (propertyName), newValue (newVal), oldValue (oldVal), isAddingNewProperty (isAdding), isDeletingProperty (isDeleting) { } bool perform() { jassert (! (isAddingNewProperty && target->hasProperty (name))); if (isDeletingProperty) target->removeProperty (name, nullptr); else target->setProperty (name, newValue, nullptr); return true; } bool undo() { if (isAddingNewProperty) target->removeProperty (name, nullptr); else target->setProperty (name, oldValue, nullptr); return true; } int getSizeInUnits() { return (int) sizeof (*this); //xxx should be more accurate } UndoableAction* createCoalescedAction (UndoableAction* nextAction) { if (! (isAddingNewProperty || isDeletingProperty)) { if (SetPropertyAction* const next = dynamic_cast (nextAction)) if (next->target == target && next->name == name && ! (next->isAddingNewProperty || next->isDeletingProperty)) return new SetPropertyAction (target, name, next->newValue, oldValue, false, false); } return nullptr; } private: const Ptr target; const Identifier name; const var newValue; var oldValue; const bool isAddingNewProperty : 1, isDeletingProperty : 1; JUCE_DECLARE_NON_COPYABLE (SetPropertyAction) }; //============================================================================== class AddOrRemoveChildAction : public UndoableAction { public: AddOrRemoveChildAction (SharedObject* parentObject, int index, SharedObject* newChild) : target (parentObject), child (newChild != nullptr ? newChild : parentObject->children.getObjectPointer (index)), childIndex (index), isDeleting (newChild == nullptr) { jassert (child != nullptr); } bool perform() { if (isDeleting) target->removeChild (childIndex, nullptr); else target->addChild (child, childIndex, nullptr); return true; } bool undo() { if (isDeleting) { target->addChild (child, childIndex, nullptr); } else { // If you hit this, it seems that your object's state is getting confused - probably // because you've interleaved some undoable and non-undoable operations? jassert (childIndex < target->children.size()); target->removeChild (childIndex, nullptr); } return true; } int getSizeInUnits() { return (int) sizeof (*this); //xxx should be more accurate } private: const Ptr target, child; const int childIndex; const bool isDeleting; JUCE_DECLARE_NON_COPYABLE (AddOrRemoveChildAction) }; //============================================================================== class MoveChildAction : public UndoableAction { public: MoveChildAction (SharedObject* parentObject, int fromIndex, int toIndex) noexcept : parent (parentObject), startIndex (fromIndex), endIndex (toIndex) { } bool perform() { parent->moveChild (startIndex, endIndex, nullptr); return true; } bool undo() { parent->moveChild (endIndex, startIndex, nullptr); return true; } int getSizeInUnits() { return (int) sizeof (*this); //xxx should be more accurate } UndoableAction* createCoalescedAction (UndoableAction* nextAction) { if (MoveChildAction* next = dynamic_cast (nextAction)) if (next->parent == parent && next->startIndex == endIndex) return new MoveChildAction (parent, startIndex, next->endIndex); return nullptr; } private: const Ptr parent; const int startIndex, endIndex; JUCE_DECLARE_NON_COPYABLE (MoveChildAction) }; //============================================================================== const Identifier type; NamedValueSet properties; ReferenceCountedArray children; SortedSet valueTreesWithListeners; SharedObject* parent; private: SharedObject& operator= (const SharedObject&); JUCE_LEAK_DETECTOR (SharedObject) }; //============================================================================== ValueTree::ValueTree() noexcept { } const ValueTree ValueTree::invalid; ValueTree::ValueTree (const Identifier& type) : object (new ValueTree::SharedObject (type)) { jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name! } ValueTree::ValueTree (SharedObject* so) : object (so) { } ValueTree::ValueTree (const ValueTree& other) : object (other.object) { } ValueTree& ValueTree::operator= (const ValueTree& other) { if (object != other.object) { if (listeners.isEmpty()) { object = other.object; } else { if (object != nullptr) object->valueTreesWithListeners.removeValue (this); if (other.object != nullptr) other.object->valueTreesWithListeners.add (this); object = other.object; listeners.call (&ValueTree::Listener::valueTreeRedirected, *this); } } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS ValueTree::ValueTree (ValueTree&& other) noexcept : object (static_cast (other.object)) { } #endif ValueTree::~ValueTree() { if (listeners.size() > 0 && object != nullptr) object->valueTreesWithListeners.removeValue (this); } bool ValueTree::operator== (const ValueTree& other) const noexcept { return object == other.object; } bool ValueTree::operator!= (const ValueTree& other) const noexcept { return object != other.object; } bool ValueTree::isEquivalentTo (const ValueTree& other) const { return object == other.object || (object != nullptr && other.object != nullptr && object->isEquivalentTo (*other.object)); } ValueTree ValueTree::createCopy() const { return ValueTree (createCopyIfNotNull (object.get())); } bool ValueTree::hasType (const Identifier& typeName) const { return object != nullptr && object->type == typeName; } Identifier ValueTree::getType() const { return object != nullptr ? object->type : Identifier(); } ValueTree ValueTree::getParent() const { return ValueTree (object != nullptr ? object->parent : static_cast (nullptr)); } ValueTree ValueTree::getSibling (const int delta) const { if (object == nullptr || object->parent == nullptr) return invalid; const int index = object->parent->indexOf (*this) + delta; return ValueTree (object->parent->children.getObjectPointer (index)); } const var& ValueTree::operator[] (const Identifier& name) const { return object == nullptr ? var::null : object->properties[name]; } const var& ValueTree::getProperty (const Identifier& name) const { return object == nullptr ? var::null : object->properties[name]; } var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const { return object == nullptr ? defaultReturnValue : object->properties.getWithDefault (name, defaultReturnValue); } ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager) { jassert (name.toString().isNotEmpty()); // Must have a valid property name! jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail! if (object != nullptr) object->setProperty (name, newValue, undoManager); return *this; } bool ValueTree::hasProperty (const Identifier& name) const { return object != nullptr && object->hasProperty (name); } void ValueTree::removeProperty (const Identifier& name, UndoManager* const undoManager) { if (object != nullptr) object->removeProperty (name, undoManager); } void ValueTree::removeAllProperties (UndoManager* const undoManager) { if (object != nullptr) object->removeAllProperties (undoManager); } int ValueTree::getNumProperties() const { return object == nullptr ? 0 : object->properties.size(); } Identifier ValueTree::getPropertyName (const int index) const { return object == nullptr ? Identifier() : object->properties.getName (index); } void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* const undoManager) { jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail! if (source.object == nullptr) removeAllProperties (undoManager); else if (object != nullptr) object->copyPropertiesFrom (*(source.object), undoManager); } int ValueTree::getReferenceCount() const noexcept { return object != nullptr ? object->getReferenceCount() : 0; } //============================================================================== class ValueTreePropertyValueSource : public Value::ValueSource, private ValueTree::Listener { public: ValueTreePropertyValueSource (const ValueTree& vt, const Identifier& prop, UndoManager* um) : tree (vt), property (prop), undoManager (um) { tree.addListener (this); } ~ValueTreePropertyValueSource() { tree.removeListener (this); } var getValue() const override { return tree [property]; } void setValue (const var& newValue) override { tree.setProperty (property, newValue, undoManager); } private: ValueTree tree; const Identifier property; UndoManager* const undoManager; void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override { if (tree == changedTree && property == changedProperty) sendChangeMessage (false); } void valueTreeChildAdded (ValueTree&, ValueTree&) override {} void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {} void valueTreeChildOrderChanged (ValueTree&, int, int) override {} void valueTreeParentChanged (ValueTree&) override {} JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource) }; Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* const undoManager) { return Value (new ValueTreePropertyValueSource (*this, name, undoManager)); } //============================================================================== int ValueTree::getNumChildren() const { return object == nullptr ? 0 : object->children.size(); } ValueTree ValueTree::getChild (int index) const { return ValueTree (object != nullptr ? object->children.getObjectPointer (index) : static_cast (nullptr)); } ValueTree ValueTree::getChildWithName (const Identifier& type) const { return object != nullptr ? object->getChildWithName (type) : ValueTree(); } ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager) { return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree(); } ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const { return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree(); } bool ValueTree::isAChildOf (const ValueTree& possibleParent) const { return object != nullptr && object->isAChildOf (possibleParent.object); } int ValueTree::indexOf (const ValueTree& child) const { return object != nullptr ? object->indexOf (child) : -1; } void ValueTree::addChild (const ValueTree& child, int index, UndoManager* const undoManager) { jassert (object != nullptr); // Trying to add a child to a null ValueTree! if (object != nullptr) object->addChild (child.object, index, undoManager); } void ValueTree::removeChild (const int childIndex, UndoManager* const undoManager) { if (object != nullptr) object->removeChild (childIndex, undoManager); } void ValueTree::removeChild (const ValueTree& child, UndoManager* const undoManager) { if (object != nullptr) object->removeChild (object->children.indexOf (child.object), undoManager); } void ValueTree::removeAllChildren (UndoManager* const undoManager) { if (object != nullptr) object->removeAllChildren (undoManager); } void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager) { if (object != nullptr) object->moveChild (currentIndex, newIndex, undoManager); } //============================================================================== void ValueTree::createListOfChildren (OwnedArray& list) const { jassert (object != nullptr); for (int i = 0; i < object->children.size(); ++i) list.add (new ValueTree (object->children.getObjectPointerUnchecked(i))); } void ValueTree::reorderChildren (const OwnedArray& newOrder, UndoManager* undoManager) { jassert (object != nullptr); object->reorderChildren (newOrder, undoManager); } //============================================================================== void ValueTree::addListener (Listener* listener) { if (listener != nullptr) { if (listeners.isEmpty() && object != nullptr) object->valueTreesWithListeners.add (this); listeners.add (listener); } } void ValueTree::removeListener (Listener* listener) { listeners.remove (listener); if (listeners.isEmpty() && object != nullptr) object->valueTreesWithListeners.removeValue (this); } void ValueTree::sendPropertyChangeMessage (const Identifier& property) { if (object != nullptr) object->sendPropertyChangeMessage (property); } //============================================================================== XmlElement* ValueTree::createXml() const { return object != nullptr ? object->createXml() : nullptr; } ValueTree ValueTree::fromXml (const XmlElement& xml) { // ValueTrees don't have any equivalent to XML text elements! jassert (! xml.isTextElement()); ValueTree v (xml.getTagName()); v.object->properties.setFromXmlAttributes (xml); forEachXmlChildElement (xml, e) v.addChild (fromXml (*e), -1, nullptr); return v; } String ValueTree::toXmlString() const { const ScopedPointer xml (createXml()); return xml != nullptr ? xml->createDocument ("") : String(); } //============================================================================== void ValueTree::writeToStream (OutputStream& output) const { SharedObject::writeObjectToStream (output, object); } ValueTree ValueTree::readFromStream (InputStream& input) { const String type (input.readString()); if (type.isEmpty()) return ValueTree(); ValueTree v (type); const int numProps = input.readCompressedInt(); if (numProps < 0) { jassertfalse; // trying to read corrupted data! return v; } for (int i = 0; i < numProps; ++i) { const String name (input.readString()); if (name.isNotEmpty()) { const var value (var::readFromStream (input)); v.object->properties.set (name, value); } else { jassertfalse; // trying to read corrupted data! } } const int numChildren = input.readCompressedInt(); v.object->children.ensureStorageAllocated (numChildren); for (int i = 0; i < numChildren; ++i) { ValueTree child (readFromStream (input)); if (! child.isValid()) return v; v.object->children.add (child.object); child.object->parent = v.object; } return v; } ValueTree ValueTree::readFromData (const void* const data, const size_t numBytes) { MemoryInputStream in (data, numBytes, false); return readFromStream (in); } ValueTree ValueTree::readFromGZIPData (const void* const data, const size_t numBytes) { MemoryInputStream in (data, numBytes, false); GZIPDecompressorInputStream gzipStream (in); return readFromStream (gzipStream); } void ValueTree::Listener::valueTreeRedirected (ValueTree&) {} //============================================================================== #if JUCE_UNIT_TESTS class ValueTreeTests : public UnitTest { public: ValueTreeTests() : UnitTest ("ValueTrees") {} static String createRandomIdentifier (Random& r) { char buffer[50] = { 0 }; const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:"; for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;) buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; return CharPointer_ASCII (buffer); } static String createRandomWideCharString (Random& r) { juce_wchar buffer[50] = { 0 }; for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;) { if (r.nextBool()) { do { buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); } while (! CharPointer_UTF16::canRepresent (buffer[i])); } else buffer[i] = (juce_wchar) (1 + r.nextInt (0x7e)); } return CharPointer_UTF32 (buffer); } static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r) { ValueTree v (createRandomIdentifier (r)); for (int i = r.nextInt (10); --i >= 0;) { switch (r.nextInt (5)) { case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break; case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break; case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break; case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break; case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break; default: break; } } return v; } void runTest() { beginTest ("ValueTree"); Random r = getRandom(); for (int i = 10; --i >= 0;) { MemoryOutputStream mo; ValueTree v1 (createRandomTree (nullptr, 0, r)); v1.writeToStream (mo); MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); ValueTree v2 = ValueTree::readFromStream (mi); expect (v1.isEquivalentTo (v2)); ScopedPointer xml1 (v1.createXml()); ScopedPointer xml2 (v2.createCopy().createXml()); expect (xml1->isEquivalentTo (xml2, false)); ValueTree v4 = v2.createCopy(); expect (v1.isEquivalentTo (v4)); } } }; static ValueTreeTests valueTreeTests; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h000066400000000000000000000602471320201440200324110ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_VALUETREE_H_INCLUDED #define JUCE_VALUETREE_H_INCLUDED //============================================================================== /** A powerful tree structure that can be used to hold free-form data, and which can handle its own undo and redo behaviour. A ValueTree contains a list of named properties as var objects, and also holds any number of sub-trees. Create ValueTree objects on the stack, and don't be afraid to copy them around, as they're simply a lightweight reference to a shared data container. Creating a copy of another ValueTree simply creates a new reference to the same underlying object - to make a separate, deep copy of a tree you should explicitly call createCopy(). Each ValueTree has a type name, in much the same way as an XmlElement has a tag name, and much of the structure of a ValueTree is similar to an XmlElement tree. You can convert a ValueTree to and from an XmlElement, and as long as the XML doesn't contain text elements, the conversion works well and makes a good serialisation format. They can also be serialised to a binary format, which is very fast and compact. All the methods that change data take an optional UndoManager, which will be used to track any changes to the object. For this to work, you have to be careful to consistently always use the same UndoManager for all operations to any node inside the tree. A ValueTree can only be a child of one parent at a time, so if you're moving one from one tree to another, be careful to always remove it first, before adding it. This could also mess up your undo/redo chain, so be wary! In a debug build you should hit assertions if you try to do anything dangerous, but there are still plenty of ways it could go wrong. Listeners can be added to a ValueTree to be told when properies change and when nodes are added or removed. @see var, XmlElement */ class JUCE_API ValueTree { public: //============================================================================== /** Creates an empty, invalid ValueTree. A ValueTree that is created with this constructor can't actually be used for anything, it's just a default 'null' ValueTree that can be returned to indicate some sort of failure. To create a real one, use the constructor that takes a string. @see ValueTree::invalid */ ValueTree() noexcept; /** Creates an empty ValueTree with the given type name. Like an XmlElement, each ValueTree node has a type, which you can access with getType() and hasType(). */ explicit ValueTree (const Identifier& type); /** Creates a reference to another ValueTree. */ ValueTree (const ValueTree&); /** Makes this object reference another node. */ ValueTree& operator= (const ValueTree&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS ValueTree (ValueTree&&) noexcept; #endif /** Destructor. */ ~ValueTree(); /** Returns true if both this and the other tree node refer to the same underlying structure. Note that this isn't a value comparison - two independently-created trees which contain identical data are not considered equal. */ bool operator== (const ValueTree&) const noexcept; /** Returns true if this and the other node refer to different underlying structures. Note that this isn't a value comparison - two independently-created trees which contain identical data are not considered equal. */ bool operator!= (const ValueTree&) const noexcept; /** Performs a deep comparison between the properties and children of two trees. If all the properties and children of the two trees are the same (recursively), this returns true. The normal operator==() only checks whether two trees refer to the same shared data structure, so use this method if you need to do a proper value comparison. */ bool isEquivalentTo (const ValueTree&) const; //============================================================================== /** Returns true if this node refers to some valid data. It's hard to create an invalid node, but you might get one returned, e.g. by an out-of-range call to getChild(). */ bool isValid() const { return object != nullptr; } /** Returns a deep copy of this tree and all its sub-nodes. */ ValueTree createCopy() const; //============================================================================== /** Returns the type of this node. The type is specified when the ValueTree is created. @see hasType */ Identifier getType() const; /** Returns true if the node has this type. The comparison is case-sensitive. */ bool hasType (const Identifier& typeName) const; //============================================================================== /** Returns the value of a named property. If no such property has been set, this will return a void variant. You can also use operator[] to get a property. @see var, setProperty, hasProperty */ const var& getProperty (const Identifier& name) const; /** Returns the value of a named property, or a user-specified default if the property doesn't exist. If no such property has been set, this will return the value of defaultReturnValue. You can also use operator[] and getProperty to get a property. @see var, getProperty, setProperty, hasProperty */ var getProperty (const Identifier& name, const var& defaultReturnValue) const; /** Returns the value of a named property. If no such property has been set, this will return a void variant. This is the same as calling getProperty(). @see getProperty */ const var& operator[] (const Identifier& name) const; /** Changes a named property of the node. The name identifier must not be an empty string. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, so that this change can be undone. @see var, getProperty, removeProperty @returns a reference to the value tree, so that you can daisy-chain calls to this method. */ ValueTree& setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager); /** Returns true if the node contains a named property. */ bool hasProperty (const Identifier& name) const; /** Removes a property from the node. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, so that this change can be undone. */ void removeProperty (const Identifier& name, UndoManager* undoManager); /** Removes all properties from the node. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, so that this change can be undone. */ void removeAllProperties (UndoManager* undoManager); /** Returns the total number of properties that the node contains. @see getProperty. */ int getNumProperties() const; /** Returns the identifier of the property with a given index. @see getNumProperties */ Identifier getPropertyName (int index) const; /** Returns a Value object that can be used to control and respond to one of the tree's properties. The Value object will maintain a reference to this tree, and will use the undo manager when it needs to change the value. Attaching a Value::Listener to the value object will provide callbacks whenever the property changes. */ Value getPropertyAsValue (const Identifier& name, UndoManager* undoManager); /** Overwrites all the properties in this tree with the properties of the source tree. Any properties that already exist will be updated; and new ones will be added, and any that are not present in the source tree will be removed. */ void copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager); //============================================================================== /** Returns the number of child nodes belonging to this one. @see getChild */ int getNumChildren() const; /** Returns one of this node's child nodes. If the index is out of range, it'll return an invalid node. (See isValid() to find out whether a node is valid). */ ValueTree getChild (int index) const; /** Returns the first child node with the speficied type name. If no such node is found, it'll return an invalid node. (See isValid() to find out whether a node is valid). @see getOrCreateChildWithName */ ValueTree getChildWithName (const Identifier& type) const; /** Returns the first child node with the speficied type name, creating and adding a child with this name if there wasn't already one there. The only time this will return an invalid object is when the object that you're calling the method on is itself invalid. @see getChildWithName */ ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); /** Looks for the first child node that has the speficied property value. This will scan the child nodes in order, until it finds one that has property that matches the specified value. If no such node is found, it'll return an invalid node. (See isValid() to find out whether a node is valid). */ ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const; /** Adds a child to this node. Make sure that the child is removed from any former parent node before calling this, or you'll hit an assertion. If the index is < 0 or greater than the current number of child nodes, the new node will be added at the end of the list. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, so that this change can be undone. */ void addChild (const ValueTree& child, int index, UndoManager* undoManager); /** Removes the specified child from this node's child-list. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, so that this change can be undone. */ void removeChild (const ValueTree& child, UndoManager* undoManager); /** Removes a child from this node's child-list. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, so that this change can be undone. */ void removeChild (int childIndex, UndoManager* undoManager); /** Removes all child-nodes from this node. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, so that this change can be undone. */ void removeAllChildren (UndoManager* undoManager); /** Moves one of the children to a different index. This will move the child to a specified index, shuffling along any intervening items as required. So for example, if you have a list of { 0, 1, 2, 3, 4, 5 }, then calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. @param currentIndex the index of the item to be moved. If this isn't a valid index, then nothing will be done @param newIndex the index at which you'd like this item to end up. If this is less than zero, the value will be moved to the end of the list @param undoManager the optional UndoManager to use to store this transaction */ void moveChild (int currentIndex, int newIndex, UndoManager* undoManager); /** Returns true if this node is anywhere below the specified parent node. This returns true if the node is a child-of-a-child, as well as a direct child. */ bool isAChildOf (const ValueTree& possibleParent) const; /** Returns the index of a child item in this parent. If the child isn't found, this returns -1. */ int indexOf (const ValueTree& child) const; /** Returns the parent node that contains this one. If the node has no parent, this will return an invalid node. (See isValid() to find out whether a node is valid). */ ValueTree getParent() const; /** Returns one of this node's siblings in its parent's child list. The delta specifies how far to move through the list, so a value of 1 would return the node that follows this one, -1 would return the node before it, 0 will return this node itself, etc. If the requested position is beyond the range of available nodes, this will return ValueTree::invalid. */ ValueTree getSibling (int delta) const; //============================================================================== /** Creates an XmlElement that holds a complete image of this node and all its children. If this node is invalid, this may return nullptr. Otherwise, the XML that is produced can be used to recreate a similar node by calling fromXml(). The caller must delete the object that is returned. @see fromXml */ XmlElement* createXml() const; /** Tries to recreate a node from its XML representation. This isn't designed to cope with random XML data - for a sensible result, it should only be fed XML that was created by the createXml() method. */ static ValueTree fromXml (const XmlElement& xml); /** This returns a string containing an XML representation of the tree. This is quite handy for debugging purposes, as it provides a quick way to view a tree. */ String toXmlString() const; //============================================================================== /** Stores this tree (and all its children) in a binary format. Once written, the data can be read back with readFromStream(). It's much faster to load/save your tree in binary form than as XML, but obviously isn't human-readable. */ void writeToStream (OutputStream& output) const; /** Reloads a tree from a stream that was written with writeToStream(). */ static ValueTree readFromStream (InputStream& input); /** Reloads a tree from a data block that was written with writeToStream(). */ static ValueTree readFromData (const void* data, size_t numBytes); /** Reloads a tree from a data block that was written with writeToStream() and then zipped using GZIPCompressorOutputStream. */ static ValueTree readFromGZIPData (const void* data, size_t numBytes); //============================================================================== /** Listener class for events that happen to a ValueTree. To get events from a ValueTree, make your class implement this interface, and use ValueTree::addListener() and ValueTree::removeListener() to register it. */ class JUCE_API Listener { public: /** Destructor. */ virtual ~Listener() {} /** This method is called when a property of this node (or of one of its sub-nodes) has changed. The tree parameter indicates which tree has had its property changed, and the property parameter indicates the property. Note that when you register a listener to a tree, it will receive this callback for property changes in that tree, and also for any of its children, (recursively, at any depth). If your tree has sub-trees but you only want to know about changes to the top level tree, simply check the tree parameter in this callback to make sure it's the tree you're interested in. */ virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) = 0; /** This method is called when a child sub-tree is added. Note that when you register a listener to a tree, it will receive this callback for child changes in both that tree and any of its children, (recursively, at any depth). If your tree has sub-trees but you only want to know about changes to the top level tree, just check the parentTree parameter to make sure it's the one that you're interested in. */ virtual void valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded) = 0; /** This method is called when a child sub-tree is removed. Note that when you register a listener to a tree, it will receive this callback for child changes in both that tree and any of its children, (recursively, at any depth). If your tree has sub-trees but you only want to know about changes to the top level tree, just check the parentTree parameter to make sure it's the one that you're interested in. */ virtual void valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved, int indexFromWhichChildWasRemoved) = 0; /** This method is called when a tree's children have been re-shuffled. Note that when you register a listener to a tree, it will receive this callback for child changes in both that tree and any of its children, (recursively, at any depth). If your tree has sub-trees but you only want to know about changes to the top level tree, just check the parameter to make sure it's the tree that you're interested in. */ virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, int oldIndex, int newIndex) = 0; /** This method is called when a tree has been added or removed from a parent node. This callback happens when the tree to which the listener was registered is added or removed from a parent. Unlike the other callbacks, it applies only to the tree to which the listener is registered, and not to any of its children. */ virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) = 0; /** This method is called when a tree is made to point to a different internal shared object. When operator= is used to make a ValueTree refer to a different object, this callback will be made. */ virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged); }; /** Adds a listener to receive callbacks when this node is changed. The listener is added to this specific ValueTree object, and not to the shared object that it refers to. When this object is deleted, all the listeners will be lost, even if other references to the same ValueTree still exist. And if you use the operator= to make this refer to a different ValueTree, any listeners will begin listening to changes to the new tree instead of the old one. When you're adding a listener, make sure that you add it to a ValueTree instance that will last for as long as you need the listener. In general, you'd never want to add a listener to a local stack-based ValueTree, and would usually add one to a member variable. @see removeListener */ void addListener (Listener* listener); /** Removes a listener that was previously added with addListener(). */ void removeListener (Listener* listener); /** Causes a property-change callback to be triggered for the specified property, calling any listeners that are registered. */ void sendPropertyChangeMessage (const Identifier& property); //============================================================================== /** This method uses a comparator object to sort the tree's children into order. The object provided must have a method of the form: @code int compareElements (const ValueTree& first, const ValueTree& second); @endcode ..and this method must return: - a value of < 0 if the first comes before the second - a value of 0 if the two objects are equivalent - a value of > 0 if the second comes before the first To improve performance, the compareElements() method can be declared as static or const. @param comparator the comparator to use for comparing elements. @param undoManager optional UndoManager for storing the changes @param retainOrderOfEquivalentItems if this is true, then items which the comparator says are equivalent will be kept in the order in which they currently appear in the array. This is slower to perform, but may be important in some cases. If it's false, a faster algorithm is used, but equivalent elements may be rearranged. */ template void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems) { if (object != nullptr) { OwnedArray sortedList; createListOfChildren (sortedList); ComparatorAdapter adapter (comparator); sortedList.sort (adapter, retainOrderOfEquivalentItems); reorderChildren (sortedList, undoManager); } } /** An invalid ValueTree that can be used if you need to return one as an error condition, etc. This invalid object is equivalent to ValueTree created with its default constructor. */ static const ValueTree invalid; /** Returns the total number of references to the shared underlying data structure that this ValueTree is using. */ int getReferenceCount() const noexcept; private: //============================================================================== JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject) friend class SharedObject; ReferenceCountedObjectPtr object; ListenerList listeners; template struct ComparatorAdapter { ComparatorAdapter (ElementComparator& comp) noexcept : comparator (comp) {} int compareElements (const ValueTree* const first, const ValueTree* const second) { return comparator.compareElements (*first, *second); } private: ElementComparator& comparator; JUCE_DECLARE_NON_COPYABLE (ComparatorAdapter) }; void createListOfChildren (OwnedArray&) const; void reorderChildren (const OwnedArray&, UndoManager*); explicit ValueTree (SharedObject*); }; #endif // JUCE_VALUETREE_H_INCLUDED juce_ValueTreeSynchroniser.cpp000066400000000000000000000152401320201440200352650ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/values/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace ValueTreeSynchroniserHelpers { enum ChangeType { propertyChanged = 1, fullSync = 2, childAdded = 3, childRemoved = 4, childMoved = 5 }; static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array& path) { while (v != topLevelTree) { ValueTree parent (v.getParent()); if (! parent.isValid()) break; path.add (parent.indexOf (v)); v = parent; } } static void writeHeader (MemoryOutputStream& stream, ChangeType type) { stream.writeByte ((char) type); } static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream, ChangeType type, ValueTree v) { writeHeader (stream, type); Array path; getValueTreePath (v, target.getRoot(), path); stream.writeCompressedInt (path.size()); for (int i = path.size(); --i >= 0;) stream.writeCompressedInt (path.getUnchecked(i)); } static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v) { const int numLevels = input.readCompressedInt(); if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check return ValueTree(); for (int i = numLevels; --i >= 0;) { const int index = input.readCompressedInt(); if (! isPositiveAndBelow (index, v.getNumChildren())) return ValueTree(); v = v.getChild (index); } return v; } } ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree) { valueTree.addListener (this); } ValueTreeSynchroniser::~ValueTreeSynchroniser() { valueTree.removeListener (this); } void ValueTreeSynchroniser::sendFullSyncCallback() { MemoryOutputStream m; writeHeader (m, ValueTreeSynchroniserHelpers::fullSync); valueTree.writeToStream (m); stateChanged (m.getData(), m.getDataSize()); } void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property) { MemoryOutputStream m; ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt); m.writeString (property.toString()); vt.getProperty (property).writeToStream (m); stateChanged (m.getData(), m.getDataSize()); } void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree) { const int index = parentTree.indexOf (childTree); jassert (index >= 0); MemoryOutputStream m; ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree); m.writeCompressedInt (index); childTree.writeToStream (m); stateChanged (m.getData(), m.getDataSize()); } void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex) { MemoryOutputStream m; ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree); m.writeCompressedInt (oldIndex); stateChanged (m.getData(), m.getDataSize()); } void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex) { MemoryOutputStream m; ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent); m.writeCompressedInt (oldIndex); m.writeCompressedInt (newIndex); stateChanged (m.getData(), m.getDataSize()); } void ValueTreeSynchroniser::valueTreeParentChanged (ValueTree&) {} // (No action needed here) bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager) { MemoryInputStream input (data, dataSize, false); const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte(); if (type == ValueTreeSynchroniserHelpers::fullSync) { root = ValueTree::readFromStream (input); return true; } ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root)); if (! v.isValid()) return false; switch (type) { case ValueTreeSynchroniserHelpers::propertyChanged: { Identifier property (input.readString()); v.setProperty (property, var::readFromStream (input), undoManager); return true; } case ValueTreeSynchroniserHelpers::childAdded: { const int index = input.readCompressedInt(); v.addChild (ValueTree::readFromStream (input), index, undoManager); return true; } case ValueTreeSynchroniserHelpers::childRemoved: { const int index = input.readCompressedInt(); if (isPositiveAndBelow (index, v.getNumChildren())) { v.removeChild (index, undoManager); return true; } jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync break; } case ValueTreeSynchroniserHelpers::childMoved: { const int oldIndex = input.readCompressedInt(); const int newIndex = input.readCompressedInt(); if (isPositiveAndBelow (oldIndex, v.getNumChildren()) && isPositiveAndBelow (newIndex, v.getNumChildren())) { v.moveChild (oldIndex, newIndex, undoManager); return true; } jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync break; } default: jassertfalse; // Seem to have received some corrupt data? break; } return false; } juce_ValueTreeSynchroniser.h000066400000000000000000000075471320201440200347450ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_data_structures/values/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_VALUETREESYNCHRONISER_H_INCLUDED #define JUCE_VALUETREESYNCHRONISER_H_INCLUDED //============================================================================== /** This class can be used to watch for all changes to the state of a ValueTree, and to convert them to a transmittable binary encoding. The purpose of this class is to allow two or more ValueTrees to be remotely synchronised by transmitting encoded changes over some kind of transport mechanism. To use it, you'll need to implement a subclass of ValueTreeSynchroniser and implement the stateChanged() method to transmit the encoded change (maybe via a network or other means) to a remote destination, where it can be applied to a target tree. */ class JUCE_API ValueTreeSynchroniser : private ValueTree::Listener { public: /** Creates a ValueTreeSynchroniser that watches the given tree. After creating an instance of this class and somehow attaching it to a target tree, you probably want to call sendFullSyncCallback() to get them into a common starting state. */ ValueTreeSynchroniser (const ValueTree& tree); /** Destructor. */ virtual ~ValueTreeSynchroniser(); /** This callback happens when the ValueTree changes and the given state-change message needs to be applied to any other trees that need to stay in sync with it. The data is an opaque blob of binary that you should transmit to wherever your target tree lives, and use applyChange() to apply this to the target tree. */ virtual void stateChanged (const void* encodedChange, size_t encodedChangeSize) = 0; /** Forces the sending of a full state message, which may be large, as it encodes the entire ValueTree. This will internally invoke stateChanged() with the encoded version of the state. */ void sendFullSyncCallback(); /** Applies an encoded change to the given destination tree. When you implement a receiver for changes that were sent by the stateChanged() message, this is the function that you'll need to call to apply them to the target tree that you want to be synced. */ static bool applyChange (ValueTree& target, const void* encodedChangeData, size_t encodedChangeDataSize, UndoManager* undoManager); /** Returns the root ValueTree that is being observed. */ const ValueTree& getRoot() noexcept { return valueTree; } private: ValueTree valueTree; void valueTreePropertyChanged (ValueTree&, const Identifier&) override; void valueTreeChildAdded (ValueTree&, ValueTree&) override; void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override; void valueTreeChildOrderChanged (ValueTree&, int, int) override; void valueTreeParentChanged (ValueTree&) override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeSynchroniser) }; #endif // JUCE_VALUETREESYNCHRONISER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/000077500000000000000000000000001320201440200241165ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/000077500000000000000000000000001320201440200265725ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp000066400000000000000000000056601320201440200337020ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class ActionBroadcaster::ActionMessage : public MessageManager::MessageBase { public: ActionMessage (const ActionBroadcaster* ab, const String& messageText, ActionListener* l) noexcept : broadcaster (const_cast (ab)), message (messageText), listener (l) {} void messageCallback() override { if (const ActionBroadcaster* const b = broadcaster) if (b->actionListeners.contains (listener)) listener->actionListenerCallback (message); } private: WeakReference broadcaster; const String message; ActionListener* const listener; JUCE_DECLARE_NON_COPYABLE (ActionMessage) }; //============================================================================== ActionBroadcaster::ActionBroadcaster() { // are you trying to create this object before or after juce has been intialised?? jassert (MessageManager::getInstanceWithoutCreating() != nullptr); } ActionBroadcaster::~ActionBroadcaster() { // all event-based objects must be deleted BEFORE juce is shut down! jassert (MessageManager::getInstanceWithoutCreating() != nullptr); masterReference.clear(); } void ActionBroadcaster::addActionListener (ActionListener* const listener) { const ScopedLock sl (actionListenerLock); if (listener != nullptr) actionListeners.add (listener); } void ActionBroadcaster::removeActionListener (ActionListener* const listener) { const ScopedLock sl (actionListenerLock); actionListeners.removeValue (listener); } void ActionBroadcaster::removeAllActionListeners() { const ScopedLock sl (actionListenerLock); actionListeners.clear(); } void ActionBroadcaster::sendActionMessage (const String& message) const { const ScopedLock sl (actionListenerLock); for (int i = actionListeners.size(); --i >= 0;) (new ActionMessage (this, message, actionListeners.getUnchecked(i)))->post(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h000066400000000000000000000055331320201440200333460ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_ACTIONBROADCASTER_H_INCLUDED #define JUCE_ACTIONBROADCASTER_H_INCLUDED //============================================================================== /** Manages a list of ActionListeners, and can send them messages. To quickly add methods to your class that can add/remove action listeners and broadcast to them, you can derive from this. @see ActionListener, ChangeListener */ class JUCE_API ActionBroadcaster { public: //============================================================================== /** Creates an ActionBroadcaster. */ ActionBroadcaster(); /** Destructor. */ virtual ~ActionBroadcaster(); //============================================================================== /** Adds a listener to the list. Trying to add a listener that's already on the list will have no effect. */ void addActionListener (ActionListener* listener); /** Removes a listener from the list. If the listener isn't on the list, this won't have any effect. */ void removeActionListener (ActionListener* listener); /** Removes all listeners from the list. */ void removeAllActionListeners(); //============================================================================== /** Broadcasts a message to all the registered listeners. @see ActionListener::actionListenerCallback */ void sendActionMessage (const String& message) const; private: //============================================================================== friend class WeakReference; WeakReference::Master masterReference; class ActionMessage; friend class ActionMessage; SortedSet actionListeners; CriticalSection actionListenerLock; JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster) }; #endif // JUCE_ACTIONBROADCASTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h000066400000000000000000000032371320201440200327010ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_ACTIONLISTENER_H_INCLUDED #define JUCE_ACTIONLISTENER_H_INCLUDED //============================================================================== /** Interface class for delivery of events that are sent by an ActionBroadcaster. @see ActionBroadcaster, ChangeListener */ class JUCE_API ActionListener { public: /** Destructor. */ virtual ~ActionListener() {} /** Overridden by your subclass to receive the callback. @param message the string that was specified when the event was triggered by a call to ActionBroadcaster::sendActionMessage() */ virtual void actionListenerCallback (const String& message) = 0; }; #endif // JUCE_ACTIONLISTENER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp000066400000000000000000000055241320201440200327140ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage { public: AsyncUpdaterMessage (AsyncUpdater& au) : owner (au) {} void messageCallback() override { if (shouldDeliver.compareAndSetBool (0, 1)) owner.handleAsyncUpdate(); } Atomic shouldDeliver; private: AsyncUpdater& owner; JUCE_DECLARE_NON_COPYABLE (AsyncUpdaterMessage) }; //============================================================================== AsyncUpdater::AsyncUpdater() { activeMessage = new AsyncUpdaterMessage (*this); } AsyncUpdater::~AsyncUpdater() { // You're deleting this object with a background thread while there's an update // pending on the main event thread - that's pretty dodgy threading, as the callback could // happen after this destructor has finished. You should either use a MessageManagerLock while // deleting this object, or find some other way to avoid such a race condition. jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); activeMessage->shouldDeliver.set (0); } void AsyncUpdater::triggerAsyncUpdate() { if (activeMessage->shouldDeliver.compareAndSetBool (1, 0)) if (! activeMessage->post()) cancelPendingUpdate(); // if the message queue fails, this avoids getting // trapped waiting for the message to arrive } void AsyncUpdater::cancelPendingUpdate() noexcept { activeMessage->shouldDeliver.set (0); } void AsyncUpdater::handleUpdateNowIfNeeded() { // This can only be called by the event thread. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); if (activeMessage->shouldDeliver.exchange (0) != 0) handleAsyncUpdate(); } bool AsyncUpdater::isUpdatePending() const noexcept { return activeMessage->shouldDeliver.value != 0; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h000066400000000000000000000102611320201440200323530ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_ASYNCUPDATER_H_INCLUDED #define JUCE_ASYNCUPDATER_H_INCLUDED //============================================================================== /** Has a callback method that is triggered asynchronously. This object allows an asynchronous callback function to be triggered, for tasks such as coalescing multiple updates into a single callback later on. Basically, one or more calls to the triggerAsyncUpdate() will result in the message thread calling handleAsyncUpdate() as soon as it can. */ class JUCE_API AsyncUpdater { public: //============================================================================== /** Creates an AsyncUpdater object. */ AsyncUpdater(); /** Destructor. If there are any pending callbacks when the object is deleted, these are lost. */ virtual ~AsyncUpdater(); //============================================================================== /** Causes the callback to be triggered at a later time. This method returns immediately, having made sure that a callback to the handleAsyncUpdate() method will occur as soon as possible. If an update callback is already pending but hasn't happened yet, calls to this method will be ignored. It's thread-safe to call this method from any number of threads without needing to worry about locking. */ void triggerAsyncUpdate(); /** This will stop any pending updates from happening. If called after triggerAsyncUpdate() and before the handleAsyncUpdate() callback happens, this will cancel the handleAsyncUpdate() callback. Note that this method simply cancels the next callback - if a callback is already in progress on a different thread, this won't block until the callback finishes, so there's no guarantee that the callback isn't still running when the method returns. */ void cancelPendingUpdate() noexcept; /** If an update has been triggered and is pending, this will invoke it synchronously. Use this as a kind of "flush" operation - if an update is pending, the handleAsyncUpdate() method will be called immediately; if no update is pending, then nothing will be done. Because this may invoke the callback, this method must only be called on the main event thread. */ void handleUpdateNowIfNeeded(); /** Returns true if there's an update callback in the pipeline. */ bool isUpdatePending() const noexcept; //============================================================================== /** Called back to do whatever your class needs to do. This method is called by the message thread at the next convenient time after the triggerAsyncUpdate() method has been called. */ virtual void handleAsyncUpdate() = 0; private: //============================================================================== class AsyncUpdaterMessage; friend class ReferenceCountedObjectPtr; ReferenceCountedObjectPtr activeMessage; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater) }; #endif // JUCE_ASYNCUPDATER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp000066400000000000000000000060201320201440200336410ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ChangeBroadcaster::ChangeBroadcaster() noexcept { callback.owner = this; } ChangeBroadcaster::~ChangeBroadcaster() { } void ChangeBroadcaster::addChangeListener (ChangeListener* const listener) { // Listeners can only be safely added when the event thread is locked // You can use a MessageManagerLock if you need to call this from another thread. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); changeListeners.add (listener); } void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener) { // Listeners can only be safely added when the event thread is locked // You can use a MessageManagerLock if you need to call this from another thread. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); changeListeners.remove (listener); } void ChangeBroadcaster::removeAllChangeListeners() { // Listeners can only be safely added when the event thread is locked // You can use a MessageManagerLock if you need to call this from another thread. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); changeListeners.clear(); } void ChangeBroadcaster::sendChangeMessage() { if (changeListeners.size() > 0) callback.triggerAsyncUpdate(); } void ChangeBroadcaster::sendSynchronousChangeMessage() { // This can only be called by the event thread. jassert (MessageManager::getInstance()->isThisTheMessageThread()); callback.cancelPendingUpdate(); callListeners(); } void ChangeBroadcaster::dispatchPendingMessages() { callback.handleUpdateNowIfNeeded(); } void ChangeBroadcaster::callListeners() { changeListeners.call (&ChangeListener::changeListenerCallback, this); } //============================================================================== ChangeBroadcaster::ChangeBroadcasterCallback::ChangeBroadcasterCallback() : owner (nullptr) { } void ChangeBroadcaster::ChangeBroadcasterCallback::handleAsyncUpdate() { jassert (owner != nullptr); owner->callListeners(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h000066400000000000000000000072331320201440200333150ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_CHANGEBROADCASTER_H_INCLUDED #define JUCE_CHANGEBROADCASTER_H_INCLUDED //============================================================================== /** Holds a list of ChangeListeners, and sends messages to them when instructed. @see ChangeListener */ class JUCE_API ChangeBroadcaster { public: //============================================================================== /** Creates an ChangeBroadcaster. */ ChangeBroadcaster() noexcept; /** Destructor. */ virtual ~ChangeBroadcaster(); //============================================================================== /** Registers a listener to receive change callbacks from this broadcaster. Trying to add a listener that's already on the list will have no effect. */ void addChangeListener (ChangeListener* listener); /** Unregisters a listener from the list. If the listener isn't on the list, this won't have any effect. */ void removeChangeListener (ChangeListener* listener); /** Removes all listeners from the list. */ void removeAllChangeListeners(); //============================================================================== /** Causes an asynchronous change message to be sent to all the registered listeners. The message will be delivered asynchronously by the main message thread, so this method will return immediately. To call the listeners synchronously use sendSynchronousChangeMessage(). */ void sendChangeMessage(); /** Sends a synchronous change message to all the registered listeners. This will immediately call all the listeners that are registered. For thread-safety reasons, you must only call this method on the main message thread. @see dispatchPendingMessages */ void sendSynchronousChangeMessage(); /** If a change message has been sent but not yet dispatched, this will call sendSynchronousChangeMessage() to make the callback immediately. For thread-safety reasons, you must only call this method on the main message thread. */ void dispatchPendingMessages(); private: //============================================================================== class ChangeBroadcasterCallback : public AsyncUpdater { public: ChangeBroadcasterCallback(); void handleAsyncUpdate() override; ChangeBroadcaster* owner; }; friend class ChangeBroadcasterCallback; ChangeBroadcasterCallback callback; ListenerList changeListeners; void callListeners(); JUCE_DECLARE_NON_COPYABLE (ChangeBroadcaster) }; #endif // JUCE_CHANGEBROADCASTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h000066400000000000000000000046131320201440200326500ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_CHANGELISTENER_H_INCLUDED #define JUCE_CHANGELISTENER_H_INCLUDED class ChangeBroadcaster; //============================================================================== /** Receives change event callbacks that are sent out by a ChangeBroadcaster. A ChangeBroadcaster keeps a set of listeners to which it broadcasts a message when the ChangeBroadcaster::sendChangeMessage() method is called. A subclass of ChangeListener is used to receive these callbacks. Note that the major difference between an ActionListener and a ChangeListener is that for a ChangeListener, multiple changes will be coalesced into fewer callbacks, but ActionListeners perform one callback for every event posted. @see ChangeBroadcaster, ActionListener */ class JUCE_API ChangeListener { public: /** Destructor. */ virtual ~ChangeListener() {} /** Your subclass should implement this method to receive the callback. @param source the ChangeBroadcaster that triggered the callback. */ virtual void changeListenerCallback (ChangeBroadcaster* source) = 0; //============================================================================== #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // This method's signature has changed to take a ChangeBroadcaster parameter - please update your code! private: virtual int changeListenerCallback (void*) { return 0; } #endif }; #endif // JUCE_CHANGELISTENER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/broadcasters/juce_ListenerList.h000066400000000000000000000364371320201440200324070ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_LISTENERLIST_H_INCLUDED #define JUCE_LISTENERLIST_H_INCLUDED //============================================================================== /** Holds a set of objects and can invoke a member function callback on each object in the set with a single call. Use a ListenerList to manage a set of objects which need a callback, and you can invoke a member function by simply calling call() or callChecked(). E.g. @code class MyListenerType { public: void myCallbackMethod (int foo, bool bar); }; ListenerList listeners; listeners.add (someCallbackObjects...); // This will invoke myCallbackMethod (1234, true) on each of the objects // in the list... listeners.call (&MyListenerType::myCallbackMethod, 1234, true); @endcode If you add or remove listeners from the list during one of the callbacks - i.e. while it's in the middle of iterating the listeners, then it's guaranteed that no listeners will be mistakenly called after they've been removed, but it may mean that some of the listeners could be called more than once, or not at all, depending on the list's order. Sometimes, there's a chance that invoking one of the callbacks might result in the list itself being deleted while it's still iterating - to survive this situation, you can use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker". The BailOutChecker must implement a method of the form "bool shouldBailOut()", and the list will check this after each callback to determine whether it should abort the operation. For an example of a bail-out checker, see the Component::BailOutChecker class, which can be used to check when a Component has been deleted. See also ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false. */ template > class ListenerList { // Horrible macros required to support VC7.. #ifndef DOXYGEN #if JUCE_VC8_OR_EARLIER #define LL_TEMPLATE(a) typename P##a, typename Q##a #define LL_PARAM(a) Q##a& param##a #else #define LL_TEMPLATE(a) typename P##a #define LL_PARAM(a) PARAMETER_TYPE(P##a) param##a #endif #endif public: //============================================================================== /** Creates an empty list. */ ListenerList() { } /** Destructor. */ ~ListenerList() { } //============================================================================== /** Adds a listener to the list. A listener can only be added once, so if the listener is already in the list, this method has no effect. @see remove */ void add (ListenerClass* const listenerToAdd) { // Listeners can't be null pointers! jassert (listenerToAdd != nullptr); if (listenerToAdd != nullptr) listeners.addIfNotAlreadyThere (listenerToAdd); } /** Removes a listener from the list. If the listener wasn't in the list, this has no effect. */ void remove (ListenerClass* const listenerToRemove) { // Listeners can't be null pointers! jassert (listenerToRemove != nullptr); listeners.removeFirstMatchingValue (listenerToRemove); } /** Returns the number of registered listeners. */ int size() const noexcept { return listeners.size(); } /** Returns true if any listeners are registered. */ bool isEmpty() const noexcept { return listeners.size() == 0; } /** Clears the list. */ void clear() { listeners.clear(); } /** Returns true if the specified listener has been added to the list. */ bool contains (ListenerClass* const listener) const noexcept { return listeners.contains (listener); } //============================================================================== /** Calls a member function on each listener in the list, with no parameters. */ void call (void (ListenerClass::*callbackFunction) ()) { callChecked (static_cast (DummyBailOutChecker()), callbackFunction); } /** Calls a member function on each listener in the list, with no parameters and a bail-out-checker. See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) ()) { for (Iterator iter (*this); iter.next (bailOutChecker);) (iter.getListener()->*callbackFunction) (); } //============================================================================== /** Calls a member function on each listener in the list, with 1 parameter. */ template void call (void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1)) { for (Iterator iter (*this); iter.next();) (iter.getListener()->*callbackFunction) (param1); } /** Calls a member function on each listener in the list, with one parameter and a bail-out-checker. See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1)) { for (Iterator iter (*this); iter.next (bailOutChecker);) (iter.getListener()->*callbackFunction) (param1); } //============================================================================== /** Calls a member function on each listener in the list, with 2 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2), LL_PARAM(1), LL_PARAM(2)) { for (Iterator iter (*this); iter.next();) (iter.getListener()->*callbackFunction) (param1, param2); } /** Calls a member function on each listener in the list, with 2 parameters and a bail-out-checker. See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2), LL_PARAM(1), LL_PARAM(2)) { for (Iterator iter (*this); iter.next (bailOutChecker);) (iter.getListener()->*callbackFunction) (param1, param2); } //============================================================================== /** Calls a member function on each listener in the list, with 3 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2, P3), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) { for (Iterator iter (*this); iter.next();) (iter.getListener()->*callbackFunction) (param1, param2, param3); } /** Calls a member function on each listener in the list, with 3 parameters and a bail-out-checker. See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2, P3), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) { for (Iterator iter (*this); iter.next (bailOutChecker);) (iter.getListener()->*callbackFunction) (param1, param2, param3); } //============================================================================== /** Calls a member function on each listener in the list, with 4 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) { for (Iterator iter (*this); iter.next();) (iter.getListener()->*callbackFunction) (param1, param2, param3, param4); } /** Calls a member function on each listener in the list, with 4 parameters and a bail-out-checker. See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) { for (Iterator iter (*this); iter.next (bailOutChecker);) (iter.getListener()->*callbackFunction) (param1, param2, param3, param4); } //============================================================================== /** Calls a member function on each listener in the list, with 5 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) { for (Iterator iter (*this); iter.next();) (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); } /** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) { for (Iterator iter (*this); iter.next (bailOutChecker);) (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); } //============================================================================== /** Calls a member function on each listener in the list, with 5 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) { for (Iterator iter (*this); iter.next();) (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); } /** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) { for (Iterator iter (*this); iter.next (bailOutChecker);) (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); } //============================================================================== /** A dummy bail-out checker that always returns false. See the ListenerList notes for more info about bail-out checkers. */ class DummyBailOutChecker { public: inline bool shouldBailOut() const noexcept { return false; } }; //============================================================================== /** Iterates the listeners in a ListenerList. */ template class Iterator { public: //============================================================================== Iterator (const ListType& listToIterate) noexcept : list (listToIterate), index (listToIterate.size()) {} ~Iterator() noexcept {} //============================================================================== bool next() noexcept { if (index <= 0) return false; const int listSize = list.size(); if (--index < listSize) return true; index = listSize - 1; return index >= 0; } bool next (const BailOutCheckerType& bailOutChecker) noexcept { return (! bailOutChecker.shouldBailOut()) && next(); } typename ListType::ListenerType* getListener() const noexcept { return list.getListeners().getUnchecked (index); } //============================================================================== private: const ListType& list; int index; JUCE_DECLARE_NON_COPYABLE (Iterator) }; typedef ListenerList ThisType; typedef ListenerClass ListenerType; const ArrayType& getListeners() const noexcept { return listeners; } private: //============================================================================== ArrayType listeners; JUCE_DECLARE_NON_COPYABLE (ListenerList) #undef LL_TEMPLATE #undef LL_PARAM }; #endif // JUCE_LISTENERLIST_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/interprocess/000077500000000000000000000000001320201440200266365ustar00rootroot00000000000000juce_ConnectedChildProcess.cpp000066400000000000000000000203611320201440200345000ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/interprocess/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ enum { magicMastSlaveConnectionHeader = 0x712baf04 }; static const char* startMessage = "__ipc_st"; static const char* killMessage = "__ipc_k_"; static const char* pingMessage = "__ipc_p_"; enum { specialMessageSize = 8, defaultTimeoutMs = 8000 }; static String getCommandLinePrefix (const String& commandLineUniqueID) { return "--" + commandLineUniqueID + ":"; } //============================================================================== // This thread sends and receives ping messages every second, so that it // can find out if the other process has stopped running. struct ChildProcessPingThread : public Thread, private AsyncUpdater { ChildProcessPingThread (int timeout) : Thread ("IPC ping"), timeoutMs (timeout) { pingReceived(); } static bool isPingMessage (const MemoryBlock& m) noexcept { return memcmp (m.getData(), pingMessage, specialMessageSize) == 0; } void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; } void triggerConnectionLostMessage() { triggerAsyncUpdate(); } virtual bool sendPingMessage (const MemoryBlock&) = 0; virtual void pingFailed() = 0; int timeoutMs; private: Atomic countdown; void handleAsyncUpdate() override { pingFailed(); } void run() override { while (! threadShouldExit()) { if (--countdown <= 0 || ! sendPingMessage (MemoryBlock (pingMessage, specialMessageSize))) { triggerConnectionLostMessage(); break; } wait (1000); } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessPingThread) }; //============================================================================== struct ChildProcessMaster::Connection : public InterprocessConnection, private ChildProcessPingThread { Connection (ChildProcessMaster& m, const String& pipeName, int timeout) : InterprocessConnection (false, magicMastSlaveConnectionHeader), ChildProcessPingThread (timeout), owner (m) { if (createPipe (pipeName, timeoutMs)) startThread (4); } ~Connection() { stopThread (10000); } private: void connectionMade() override {} void connectionLost() override { owner.handleConnectionLost(); } bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); } void pingFailed() override { connectionLost(); } void messageReceived (const MemoryBlock& m) override { pingReceived(); if (m.getSize() != specialMessageSize || ! isPingMessage (m)) owner.handleMessageFromSlave (m); } ChildProcessMaster& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) }; //============================================================================== ChildProcessMaster::ChildProcessMaster() {} ChildProcessMaster::~ChildProcessMaster() { if (connection != nullptr) { sendMessageToSlave (MemoryBlock (killMessage, specialMessageSize)); connection->disconnect(); connection = nullptr; } } void ChildProcessMaster::handleConnectionLost() {} bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) { if (connection != nullptr) return connection->sendMessage (mb); jassertfalse; // this can only be used when the connection is active! return false; } bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID, int timeoutMs) { connection = nullptr; jassert (childProcess.kill()); const String pipeName ("p" + String::toHexString (Random().nextInt64())); StringArray args; args.add (executable.getFullPathName()); args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName); if (childProcess.start (args)) { connection = new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs); if (connection->isConnected()) { sendMessageToSlave (MemoryBlock (startMessage, specialMessageSize)); return true; } connection = nullptr; } return false; } //============================================================================== struct ChildProcessSlave::Connection : public InterprocessConnection, private ChildProcessPingThread { Connection (ChildProcessSlave& p, const String& pipeName, int timeout) : InterprocessConnection (false, magicMastSlaveConnectionHeader), ChildProcessPingThread (timeout), owner (p) { connectToPipe (pipeName, timeoutMs); startThread (4); } ~Connection() { stopThread (10000); } private: ChildProcessSlave& owner; void connectionMade() override {} void connectionLost() override { owner.handleConnectionLost(); } bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); } void pingFailed() override { connectionLost(); } void messageReceived (const MemoryBlock& m) override { pingReceived(); if (m.getSize() == specialMessageSize) { if (isPingMessage (m)) return; if (memcmp (m.getData(), killMessage, specialMessageSize) == 0) { triggerConnectionLostMessage(); return; } if (memcmp (m.getData(), startMessage, specialMessageSize) == 0) { owner.handleConnectionMade(); return; } } owner.handleMessageFromMaster (m); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) }; //============================================================================== ChildProcessSlave::ChildProcessSlave() {} ChildProcessSlave::~ChildProcessSlave() {} void ChildProcessSlave::handleConnectionMade() {} void ChildProcessSlave::handleConnectionLost() {} bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) { if (connection != nullptr) return connection->sendMessage (mb); jassertfalse; // this can only be used when the connection is active! return false; } bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, const String& commandLineUniqueID, int timeoutMs) { String prefix (getCommandLinePrefix (commandLineUniqueID)); if (commandLine.trim().startsWith (prefix)) { String pipeName (commandLine.fromFirstOccurrenceOf (prefix, false, false) .upToFirstOccurrenceOf (" ", false, false).trim()); if (pipeName.isNotEmpty()) { connection = new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs); if (! connection->isConnected()) connection = nullptr; } } return connection != nullptr; } juce_ConnectedChildProcess.h000066400000000000000000000205431320201440200341470ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/interprocess/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED #define JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED //============================================================================== /** Acts as the slave end of a master/slave pair of connected processes. The ChildProcessSlave and ChildProcessMaster classes make it easy for an app to spawn a child process, and to manage a 2-way messaging connection to control it. To use the system, you need to create subclasses of both ChildProcessSlave and ChildProcessMaster. To instantiate the ChildProcessSlave object, you must add some code to your main() or JUCEApplication::initialise() function that calls the initialiseFromCommandLine() method to check the app's command-line parameters to see whether it's being launched as a child process. If this returns true then the slave process can be allowed to run, and its handleMessageFromMaster() method will be called whenever a message arrives. The juce demo app has a good example of this class in action. @see ChildProcessMaster, InterprocessConnection, ChildProcess */ class JUCE_API ChildProcessSlave { public: /** Creates a non-connected slave process. Use initialiseFromCommandLine to connect to a master process. */ ChildProcessSlave(); /** Destructor. */ virtual ~ChildProcessSlave(); /** This checks some command-line parameters to see whether they were generated by ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process. In an exe that can be used as a child process, you should add some code to your main() or JUCEApplication::initialise() that calls this method. The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) that matches the string passed to ChildProcessMaster::launchSlaveProcess(). The timeoutMs parameter lets you specify how long the child process is allowed to run without receiving a ping from the master before the master is considered to have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout makes it use a default value. Returns true if the command-line matches and the connection is made successfully. */ bool initialiseFromCommandLine (const String& commandLine, const String& commandLineUniqueID, int timeoutMs = 0); //============================================================================== /** This will be called to deliver messages from the master process. The call will probably be made on a background thread, so be careful with your thread-safety! You may want to respond by sending back a message with sendMessageToMaster() */ virtual void handleMessageFromMaster (const MemoryBlock&) = 0; /** This will be called when the master process finishes connecting to this slave. The call will probably be made on a background thread, so be careful with your thread-safety! */ virtual void handleConnectionMade(); /** This will be called when the connection to the master process is lost. The call may be made from any thread (including the message thread). Typically, if your process only exists to act as a slave, you should probably exit when this happens. */ virtual void handleConnectionLost(); /** Tries to send a message to the master process. This returns true if the message was sent, but doesn't check that it actually gets delivered at the other end. If successful, the data will emerge in a call to your ChildProcessMaster::handleMessageFromSlave(). */ bool sendMessageToMaster (const MemoryBlock&); private: struct Connection; friend struct Connection; friend struct ContainerDeletePolicy; ScopedPointer connection; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave) }; //============================================================================== /** Acts as the master in a master/slave pair of connected processes. The ChildProcessSlave and ChildProcessMaster classes make it easy for an app to spawn a child process, and to manage a 2-way messaging connection to control it. To use the system, you need to create subclasses of both ChildProcessSlave and ChildProcessMaster. When you want your master process to launch the slave, you just call launchSlaveProcess(), and it'll attempt to launch the executable that you specify (which may be the same exe), and assuming it has been set-up to correctly parse the command-line parameters (see ChildProcessSlave) then a two-way connection will be created. The juce demo app has a good example of this class in action. @see ChildProcessSlave, InterprocessConnection, ChildProcess */ class JUCE_API ChildProcessMaster { public: /** Creates an uninitialised master process object. Use launchSlaveProcess to launch and connect to a child process. */ ChildProcessMaster(); /** Destructor. */ virtual ~ChildProcessMaster(); /** Attempts to launch and connect to a slave process. This will start the given executable, passing it a special command-line parameter based around the commandLineUniqueID string, which must be a short alphanumeric string (no spaces!) that identifies your app. The exe that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() in its startup code, and must use a matching ID to commandLineUniqueID. The timeoutMs parameter lets you specify how long the child process is allowed to go without sending a ping before it is considered to have died and handleConnectionLost() will be called. Passing <= 0 for this timeout makes it use a default value. If this all works, the method returns true, and you can begin sending and receiving messages with the slave process. */ bool launchSlaveProcess (const File& executableToLaunch, const String& commandLineUniqueID, int timeoutMs = 0); /** This will be called to deliver a message from the slave process. The call will probably be made on a background thread, so be careful with your thread-safety! */ virtual void handleMessageFromSlave (const MemoryBlock&) = 0; /** This will be called when the slave process dies or is somehow disconnected. The call will probably be made on a background thread, so be careful with your thread-safety! */ virtual void handleConnectionLost(); /** Attempts to send a message to the slave process. This returns true if the message was dispatched, but doesn't check that it actually gets delivered at the other end. If successful, the data will emerge in a call to your ChildProcessSlave::handleMessageFromMaster(). */ bool sendMessageToSlave (const MemoryBlock&); private: ChildProcess childProcess; struct Connection; friend struct Connection; friend struct ContainerDeletePolicy; ScopedPointer connection; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster) }; #endif // JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED juce_InterprocessConnection.cpp000066400000000000000000000244311320201440200347750ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/interprocess/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct InterprocessConnection::ConnectionThread : public Thread { ConnectionThread (InterprocessConnection& c) : Thread ("JUCE IPC"), owner (c) {} void run() override { owner.runThread(); } private: InterprocessConnection& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionThread) }; //============================================================================== InterprocessConnection::InterprocessConnection (const bool callbacksOnMessageThread, const uint32 magicMessageHeaderNumber) : callbackConnectionState (false), useMessageThread (callbacksOnMessageThread), magicMessageHeader (magicMessageHeaderNumber), pipeReceiveMessageTimeout (-1) { thread = new ConnectionThread (*this); } InterprocessConnection::~InterprocessConnection() { callbackConnectionState = false; disconnect(); masterReference.clear(); thread = nullptr; } //============================================================================== bool InterprocessConnection::connectToSocket (const String& hostName, const int portNumber, const int timeOutMillisecs) { disconnect(); const ScopedLock sl (pipeAndSocketLock); socket = new StreamingSocket(); if (socket->connect (hostName, portNumber, timeOutMillisecs)) { connectionMadeInt(); thread->startThread(); return true; } socket = nullptr; return false; } bool InterprocessConnection::connectToPipe (const String& pipeName, const int timeoutMs) { disconnect(); ScopedPointer newPipe (new NamedPipe()); if (newPipe->openExisting (pipeName)) { const ScopedLock sl (pipeAndSocketLock); pipeReceiveMessageTimeout = timeoutMs; initialiseWithPipe (newPipe.release()); return true; } return false; } bool InterprocessConnection::createPipe (const String& pipeName, const int timeoutMs) { disconnect(); ScopedPointer newPipe (new NamedPipe()); if (newPipe->createNewPipe (pipeName)) { const ScopedLock sl (pipeAndSocketLock); pipeReceiveMessageTimeout = timeoutMs; initialiseWithPipe (newPipe.release()); return true; } return false; } void InterprocessConnection::disconnect() { thread->signalThreadShouldExit(); { const ScopedLock sl (pipeAndSocketLock); if (socket != nullptr) socket->close(); if (pipe != nullptr) pipe->close(); } thread->stopThread (4000); deletePipeAndSocket(); connectionLostInt(); } void InterprocessConnection::deletePipeAndSocket() { const ScopedLock sl (pipeAndSocketLock); socket = nullptr; pipe = nullptr; } bool InterprocessConnection::isConnected() const { const ScopedLock sl (pipeAndSocketLock); return ((socket != nullptr && socket->isConnected()) || (pipe != nullptr && pipe->isOpen())) && thread->isThreadRunning(); } String InterprocessConnection::getConnectedHostName() const { { const ScopedLock sl (pipeAndSocketLock); if (pipe == nullptr && socket == nullptr) return String(); if (socket != nullptr && ! socket->isLocal()) return socket->getHostName(); } return IPAddress::local().toString(); } //============================================================================== bool InterprocessConnection::sendMessage (const MemoryBlock& message) { uint32 messageHeader[2] = { ByteOrder::swapIfBigEndian (magicMessageHeader), ByteOrder::swapIfBigEndian ((uint32) message.getSize()) }; MemoryBlock messageData (sizeof (messageHeader) + message.getSize()); messageData.copyFrom (messageHeader, 0, sizeof (messageHeader)); messageData.copyFrom (message.getData(), sizeof (messageHeader), message.getSize()); return writeData (messageData.getData(), (int) messageData.getSize()) == (int) messageData.getSize(); } int InterprocessConnection::writeData (void* data, int dataSize) { const ScopedLock sl (pipeAndSocketLock); if (socket != nullptr) return socket->write (data, dataSize); if (pipe != nullptr) return pipe->write (data, dataSize, pipeReceiveMessageTimeout); return 0; } //============================================================================== void InterprocessConnection::initialiseWithSocket (StreamingSocket* newSocket) { jassert (socket == nullptr && pipe == nullptr); socket = newSocket; connectionMadeInt(); thread->startThread(); } void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe) { jassert (socket == nullptr && pipe == nullptr); pipe = newPipe; connectionMadeInt(); thread->startThread(); } //============================================================================== struct ConnectionStateMessage : public MessageManager::MessageBase { ConnectionStateMessage (InterprocessConnection* ipc, bool connected) noexcept : owner (ipc), connectionMade (connected) {} void messageCallback() override { if (InterprocessConnection* const ipc = owner) { if (connectionMade) ipc->connectionMade(); else ipc->connectionLost(); } } WeakReference owner; bool connectionMade; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionStateMessage) }; void InterprocessConnection::connectionMadeInt() { if (! callbackConnectionState) { callbackConnectionState = true; if (useMessageThread) (new ConnectionStateMessage (this, true))->post(); else connectionMade(); } } void InterprocessConnection::connectionLostInt() { if (callbackConnectionState) { callbackConnectionState = false; if (useMessageThread) (new ConnectionStateMessage (this, false))->post(); else connectionLost(); } } struct DataDeliveryMessage : public Message { DataDeliveryMessage (InterprocessConnection* ipc, const MemoryBlock& d) : owner (ipc), data (d) {} void messageCallback() override { if (InterprocessConnection* const ipc = owner) ipc->messageReceived (data); } WeakReference owner; MemoryBlock data; }; void InterprocessConnection::deliverDataInt (const MemoryBlock& data) { jassert (callbackConnectionState); if (useMessageThread) (new DataDeliveryMessage (this, data))->post(); else messageReceived (data); } //============================================================================== bool InterprocessConnection::readNextMessageInt() { uint32 messageHeader[2]; const int bytes = socket != nullptr ? socket->read (messageHeader, sizeof (messageHeader), true) : pipe ->read (messageHeader, sizeof (messageHeader), -1); if (bytes == sizeof (messageHeader) && ByteOrder::swapIfBigEndian (messageHeader[0]) == magicMessageHeader) { int bytesInMessage = (int) ByteOrder::swapIfBigEndian (messageHeader[1]); if (bytesInMessage > 0) { MemoryBlock messageData ((size_t) bytesInMessage, true); int bytesRead = 0; while (bytesInMessage > 0) { if (thread->threadShouldExit()) return false; const int numThisTime = jmin (bytesInMessage, 65536); void* const data = addBytesToPointer (messageData.getData(), bytesRead); const int bytesIn = socket != nullptr ? socket->read (data, numThisTime, true) : pipe ->read (data, numThisTime, -1); if (bytesIn <= 0) break; bytesRead += bytesIn; bytesInMessage -= bytesIn; } if (bytesRead >= 0) deliverDataInt (messageData); } } else if (bytes < 0) { if (socket != nullptr) deletePipeAndSocket(); connectionLostInt(); return false; } return true; } void InterprocessConnection::runThread() { while (! thread->threadShouldExit()) { if (socket != nullptr) { const int ready = socket->waitUntilReady (true, 0); if (ready < 0) { deletePipeAndSocket(); connectionLostInt(); break; } if (ready == 0) { thread->wait (1); continue; } } else if (pipe != nullptr) { if (! pipe->isOpen()) { deletePipeAndSocket(); connectionLostInt(); break; } } else { break; } if (thread->threadShouldExit() || ! readNextMessageInt()) break; } } juce_InterprocessConnection.h000066400000000000000000000217451320201440200344470ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/interprocess/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_INTERPROCESSCONNECTION_H_INCLUDED #define JUCE_INTERPROCESSCONNECTION_H_INCLUDED class InterprocessConnectionServer; class MemoryBlock; //============================================================================== /** Manages a simple two-way messaging connection to another process, using either a socket or a named pipe as the transport medium. To connect to a waiting socket or an open pipe, use the connectToSocket() or connectToPipe() methods. If this succeeds, messages can be sent to the other end, and incoming messages will result in a callback via the messageReceived() method. To open a pipe and wait for another client to connect to it, use the createPipe() method. To act as a socket server and create connections for one or more client, see the InterprocessConnectionServer class. @see InterprocessConnectionServer, Socket, NamedPipe */ class JUCE_API InterprocessConnection { public: //============================================================================== /** Creates a connection. Connections are created manually, connecting them with the connectToSocket() or connectToPipe() methods, or they are created automatically by a InterprocessConnectionServer when a client wants to connect. @param callbacksOnMessageThread if true, callbacks to the connectionMade(), connectionLost() and messageReceived() methods will always be made using the message thread; if false, these will be called immediately on the connection's own thread. @param magicMessageHeaderNumber a magic number to use in the header to check the validity of the data blocks being sent and received. This can be any number, but the sender and receiver must obviously use matching values or they won't recognise each other. */ InterprocessConnection (bool callbacksOnMessageThread = true, uint32 magicMessageHeaderNumber = 0xf2b49e2c); /** Destructor. */ virtual ~InterprocessConnection(); //============================================================================== /** Tries to connect this object to a socket. For this to work, the machine on the other end needs to have a InterprocessConnectionServer object waiting to receive client connections on this port number. @param hostName the host computer, either a network address or name @param portNumber the socket port number to try to connect to @param timeOutMillisecs how long to keep trying before giving up @returns true if the connection is established successfully @see Socket */ bool connectToSocket (const String& hostName, int portNumber, int timeOutMillisecs); /** Tries to connect the object to an existing named pipe. For this to work, another process on the same computer must already have opened an InterprocessConnection object and used createPipe() to create a pipe for this to connect to. @param pipeName the name to use for the pipe - this should be unique to your app @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing to the pipe, or -1 for an infinite timeout. @returns true if it connects successfully. @see createPipe, NamedPipe */ bool connectToPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs); /** Tries to create a new pipe for other processes to connect to. This creates a pipe with the given name, so that other processes can use connectToPipe() to connect to the other end. @param pipeName the name to use for the pipe - this should be unique to your app @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing to the pipe, or -1 for an infinite timeout. @returns true if the pipe was created, or false if it fails (e.g. if another process is already using using the pipe). */ bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs); /** Disconnects and closes any currently-open sockets or pipes. */ void disconnect(); /** True if a socket or pipe is currently active. */ bool isConnected() const; /** Returns the socket that this connection is using (or nullptr if it uses a pipe). */ StreamingSocket* getSocket() const noexcept { return socket; } /** Returns the pipe that this connection is using (or nullptr if it uses a socket). */ NamedPipe* getPipe() const noexcept { return pipe; } /** Returns the name of the machine at the other end of this connection. This may return an empty string if the name is unknown. */ String getConnectedHostName() const; //============================================================================== /** Tries to send a message to the other end of this connection. This will fail if it's not connected, or if there's some kind of write error. If it succeeds, the connection object at the other end will receive the message by a callback to its messageReceived() method. @see messageReceived */ bool sendMessage (const MemoryBlock& message); //============================================================================== /** Called when the connection is first connected. If the connection was created with the callbacksOnMessageThread flag set, then this will be called on the message thread; otherwise it will be called on a server thread. */ virtual void connectionMade() = 0; /** Called when the connection is broken. If the connection was created with the callbacksOnMessageThread flag set, then this will be called on the message thread; otherwise it will be called on a server thread. */ virtual void connectionLost() = 0; /** Called when a message arrives. When the object at the other end of this connection sends us a message with sendMessage(), this callback is used to deliver it to us. If the connection was created with the callbacksOnMessageThread flag set, then this will be called on the message thread; otherwise it will be called on a server thread. @see sendMessage */ virtual void messageReceived (const MemoryBlock& message) = 0; private: //============================================================================== WeakReference::Master masterReference; friend class WeakReference; CriticalSection pipeAndSocketLock; ScopedPointer socket; ScopedPointer pipe; bool callbackConnectionState; const bool useMessageThread; const uint32 magicMessageHeader; int pipeReceiveMessageTimeout; friend class InterprocessConnectionServer; void initialiseWithSocket (StreamingSocket*); void initialiseWithPipe (NamedPipe*); void deletePipeAndSocket(); void connectionMadeInt(); void connectionLostInt(); void deliverDataInt (const MemoryBlock&); bool readNextMessageInt(); struct ConnectionThread; friend struct ConnectionThread; friend struct ContainerDeletePolicy; ScopedPointer thread; void runThread(); int writeData (void*, int); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection) }; #endif // JUCE_INTERPROCESSCONNECTION_H_INCLUDED juce_InterprocessConnectionServer.cpp000066400000000000000000000040521320201440200361610ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/interprocess/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ InterprocessConnectionServer::InterprocessConnectionServer() : Thread ("Juce IPC server") { } InterprocessConnectionServer::~InterprocessConnectionServer() { stop(); } //============================================================================== bool InterprocessConnectionServer::beginWaitingForSocket (const int portNumber) { stop(); socket = new StreamingSocket(); if (socket->createListener (portNumber)) { startThread(); return true; } socket = nullptr; return false; } void InterprocessConnectionServer::stop() { signalThreadShouldExit(); if (socket != nullptr) socket->close(); stopThread (4000); socket = nullptr; } void InterprocessConnectionServer::run() { while ((! threadShouldExit()) && socket != nullptr) { ScopedPointer clientSocket (socket->waitForNextConnection()); if (clientSocket != nullptr) if (InterprocessConnection* newConnection = createConnectionObject()) newConnection->initialiseWithSocket (clientSocket.release()); } } juce_InterprocessConnectionServer.h000066400000000000000000000063211320201440200356270ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/interprocess/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED #define JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED //============================================================================== /** An object that waits for client sockets to connect to a port on this host, and creates InterprocessConnection objects for each one. To use this, create a class derived from it which implements the createConnectionObject() method, so that it creates suitable connection objects for each client that tries to connect. @see InterprocessConnection */ class JUCE_API InterprocessConnectionServer : private Thread { public: //============================================================================== /** Creates an uninitialised server object. */ InterprocessConnectionServer(); /** Destructor. */ ~InterprocessConnectionServer(); //============================================================================== /** Starts an internal thread which listens on the given port number. While this is running, in another process tries to connect with the InterprocessConnection::connectToSocket() method, this object will call createConnectionObject() to create a connection to that client. Use stop() to stop the thread running. @see createConnectionObject, stop */ bool beginWaitingForSocket (int portNumber); /** Terminates the listener thread, if it's active. @see beginWaitingForSocket */ void stop(); protected: /** Creates a suitable connection object for a client process that wants to connect to this one. This will be called by the listener thread when a client process tries to connect, and must return a new InterprocessConnection object that will then run as this end of the connection. @see InterprocessConnection */ virtual InterprocessConnection* createConnectionObject() = 0; private: //============================================================================== ScopedPointer socket; void run() override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnectionServer) }; #endif // JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/juce_events.cpp000066400000000000000000000071741320201440200271450ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if defined (JUCE_EVENTS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_events.h" #if JUCE_CATCH_UNHANDLED_EXCEPTIONS && JUCE_MODULE_AVAILABLE_juce_gui_basics #include "../juce_gui_basics/juce_gui_basics.h" #endif //============================================================================== #if JUCE_MAC #import #import #import #import #import #elif JUCE_LINUX #include #include #include #undef KeyPress #include #endif //============================================================================== namespace juce { #include "messages/juce_ApplicationBase.cpp" #include "messages/juce_DeletedAtShutdown.cpp" #include "messages/juce_MessageListener.cpp" #include "messages/juce_MessageManager.cpp" #include "broadcasters/juce_ActionBroadcaster.cpp" #include "broadcasters/juce_AsyncUpdater.cpp" #include "broadcasters/juce_ChangeBroadcaster.cpp" #include "timers/juce_MultiTimer.cpp" #include "timers/juce_Timer.cpp" #include "interprocess/juce_InterprocessConnection.cpp" #include "interprocess/juce_InterprocessConnectionServer.cpp" #include "interprocess/juce_ConnectedChildProcess.cpp" //============================================================================== #if JUCE_MAC #include "../juce_core/native/juce_osx_ObjCHelpers.h" #include "native/juce_osx_MessageQueue.h" #include "native/juce_mac_MessageManager.mm" #elif JUCE_IOS #include "../juce_core/native/juce_osx_ObjCHelpers.h" #include "native/juce_osx_MessageQueue.h" #include "native/juce_ios_MessageManager.mm" #elif JUCE_WINDOWS #include "native/juce_win32_HiddenMessageWindow.h" #include "native/juce_win32_Messaging.cpp" #elif JUCE_LINUX #include "native/juce_ScopedXLock.h" #include "native/juce_linux_Messaging.cpp" #elif JUCE_ANDROID #include "../juce_core/native/juce_android_JNIHelpers.h" #include "native/juce_android_Messaging.cpp" #endif } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/juce_events.h000066400000000000000000000041431320201440200266030ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_EVENTS_H_INCLUDED #define JUCE_EVENTS_H_INCLUDED //============================================================================= #include "../juce_core/juce_core.h" namespace juce { #include "messages/juce_MessageManager.h" #include "messages/juce_Message.h" #include "messages/juce_MessageListener.h" #include "messages/juce_CallbackMessage.h" #include "messages/juce_DeletedAtShutdown.h" #include "messages/juce_NotificationType.h" #include "messages/juce_ApplicationBase.h" #include "messages/juce_Initialisation.h" #include "messages/juce_MountedVolumeListChangeDetector.h" #include "broadcasters/juce_ListenerList.h" #include "broadcasters/juce_ActionBroadcaster.h" #include "broadcasters/juce_ActionListener.h" #include "broadcasters/juce_AsyncUpdater.h" #include "broadcasters/juce_ChangeListener.h" #include "broadcasters/juce_ChangeBroadcaster.h" #include "timers/juce_Timer.h" #include "timers/juce_MultiTimer.h" #include "interprocess/juce_InterprocessConnection.h" #include "interprocess/juce_InterprocessConnectionServer.h" #include "interprocess/juce_ConnectedChildProcess.h" #include "native/juce_ScopedXLock.h" } #endif // JUCE_EVENTS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/juce_events.mm000066400000000000000000000016771320201440200267760ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_events.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/juce_module_info000066400000000000000000000015031320201440200273460ustar00rootroot00000000000000{ "id": "juce_events", "name": "JUCE message and event handling classes", "version": "3.2.0", "description": "Classes for running an application's main event loop and sending/receiving messages, timers, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", "dependencies": [ { "id": "juce_core", "version": "matching" } ], "include": "juce_events.h", "compile": [ { "file": "juce_events.cpp", "target": "! xcode" }, { "file": "juce_events.mm", "target": "xcode" } ], "browse": [ "messages/*", "timers/*", "broadcasters/*", "interprocess/*", "native/*" ], "LinuxLibs": "X11" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/000077500000000000000000000000001320201440200257255ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp000066400000000000000000000201471320201440200325010ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ JUCEApplicationBase::CreateInstanceFunction JUCEApplicationBase::createInstance = 0; JUCEApplicationBase* JUCEApplicationBase::appInstance = nullptr; JUCEApplicationBase::JUCEApplicationBase() : appReturnValue (0), stillInitialising (true) { jassert (isStandaloneApp() && appInstance == nullptr); appInstance = this; } JUCEApplicationBase::~JUCEApplicationBase() { jassert (appInstance == this); appInstance = nullptr; } void JUCEApplicationBase::setApplicationReturnValue (const int newReturnValue) noexcept { appReturnValue = newReturnValue; } // This is called on the Mac and iOS where the OS doesn't allow the stack to unwind on shutdown.. void JUCEApplicationBase::appWillTerminateByForce() { JUCE_AUTORELEASEPOOL { { const ScopedPointer app (appInstance); if (app != nullptr) app->shutdownApp(); } DeletedAtShutdown::deleteAll(); MessageManager::deleteInstance(); } } void JUCEApplicationBase::quit() { MessageManager::getInstance()->stopDispatchLoop(); } void JUCEApplicationBase::sendUnhandledException (const std::exception* const e, const char* const sourceFile, const int lineNumber) { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) app->unhandledException (e, sourceFile, lineNumber); } //============================================================================== #if ! (JUCE_IOS || JUCE_ANDROID) #define JUCE_HANDLE_MULTIPLE_INSTANCES 1 #endif #if JUCE_HANDLE_MULTIPLE_INSTANCES struct JUCEApplicationBase::MultipleInstanceHandler : public ActionListener { public: MultipleInstanceHandler (const String& appName) : appLock ("juceAppLock_" + appName) { } bool sendCommandLineToPreexistingInstance() { if (appLock.enter (0)) return false; JUCEApplicationBase* const app = JUCEApplicationBase::getInstance(); jassert (app != nullptr); MessageManager::broadcastMessage (app->getApplicationName() + "/" + app->getCommandLineParameters()); return true; } void actionListenerCallback (const String& message) override { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) { const String appName (app->getApplicationName()); if (message.startsWith (appName + "/")) app->anotherInstanceStarted (message.substring (appName.length() + 1)); } } private: InterProcessLock appLock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultipleInstanceHandler) }; bool JUCEApplicationBase::sendCommandLineToPreexistingInstance() { jassert (multipleInstanceHandler == nullptr); // this must only be called once! multipleInstanceHandler = new MultipleInstanceHandler (getApplicationName()); return multipleInstanceHandler->sendCommandLineToPreexistingInstance(); } #else struct JUCEApplicationBase::MultipleInstanceHandler {}; #endif //============================================================================== #if JUCE_ANDROID StringArray JUCEApplicationBase::getCommandLineParameterArray() { return StringArray(); } String JUCEApplicationBase::getCommandLineParameters() { return String(); } #else #if JUCE_WINDOWS && ! defined (_CONSOLE) String JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameters() { return CharacterFunctions::findEndOfToken (CharPointer_UTF16 (GetCommandLineW()), CharPointer_UTF16 (L" "), CharPointer_UTF16 (L"\"")).findEndOfWhitespace(); } StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray() { StringArray s; int argc = 0; if (LPWSTR* const argv = CommandLineToArgvW (GetCommandLineW(), &argc)) { s = StringArray (argv + 1, argc - 1); LocalFree (argv); } return s; } #else #if JUCE_IOS extern int juce_iOSMain (int argc, const char* argv[]); #endif #if JUCE_MAC extern void initialiseNSApplication(); #endif #if JUCE_WINDOWS const char* const* juce_argv = nullptr; int juce_argc = 0; #else extern const char* const* juce_argv; // declared in juce_core extern int juce_argc; #endif String JUCEApplicationBase::getCommandLineParameters() { String argString; for (int i = 1; i < juce_argc; ++i) { String arg (juce_argv[i]); if (arg.containsChar (' ') && ! arg.isQuotedString()) arg = arg.quoted ('"'); argString << arg << ' '; } return argString.trim(); } StringArray JUCEApplicationBase::getCommandLineParameterArray() { return StringArray (juce_argv + 1, juce_argc - 1); } int JUCEApplicationBase::main (int argc, const char* argv[]) { JUCE_AUTORELEASEPOOL { juce_argc = argc; juce_argv = argv; #if JUCE_MAC initialiseNSApplication(); #endif #if JUCE_IOS return juce_iOSMain (argc, argv); #else return JUCEApplicationBase::main(); #endif } } #endif //============================================================================== int JUCEApplicationBase::main() { ScopedJuceInitialiser_GUI libraryInitialiser; jassert (createInstance != nullptr); const ScopedPointer app (createInstance()); jassert (app != nullptr); if (! app->initialiseApp()) return app->shutdownApp(); JUCE_TRY { // loop until a quit message is received.. MessageManager::getInstance()->runDispatchLoop(); } JUCE_CATCH_EXCEPTION return app->shutdownApp(); } #endif //============================================================================== bool JUCEApplicationBase::initialiseApp() { #if JUCE_HANDLE_MULTIPLE_INSTANCES if ((! moreThanOneInstanceAllowed()) && sendCommandLineToPreexistingInstance()) { DBG ("Another instance is running - quitting..."); return false; } #endif // let the app do its setting-up.. initialise (getCommandLineParameters()); stillInitialising = false; if (MessageManager::getInstance()->hasStopMessageBeenSent()) return false; #if JUCE_HANDLE_MULTIPLE_INSTANCES if (multipleInstanceHandler != nullptr) MessageManager::getInstance()->registerBroadcastListener (multipleInstanceHandler); #endif return true; } int JUCEApplicationBase::shutdownApp() { jassert (JUCEApplicationBase::getInstance() == this); #if JUCE_HANDLE_MULTIPLE_INSTANCES if (multipleInstanceHandler != nullptr) MessageManager::getInstance()->deregisterBroadcastListener (multipleInstanceHandler); #endif JUCE_TRY { // give the app a chance to clean up.. shutdown(); } JUCE_CATCH_EXCEPTION multipleInstanceHandler = nullptr; return getApplicationReturnValue(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h000066400000000000000000000265761320201440200321620ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_APPLICATIONBASE_H_INCLUDED #define JUCE_APPLICATIONBASE_H_INCLUDED //============================================================================== /** Abstract base class for application classes. Note that in the juce_gui_basics module, there's a utility class JUCEApplication which derives from JUCEApplicationBase, and takes care of a few chores. Most of the time you'll want to derive your class from JUCEApplication rather than using JUCEApplicationBase directly, but if you're not using the juce_gui_basics module then you might need to go straight to this base class. Any application that wants to run an event loop must declare a subclass of JUCEApplicationBase, and implement its various pure virtual methods. It then needs to use the START_JUCE_APPLICATION macro somewhere in a CPP file to declare an instance of this class and generate suitable platform-specific boilerplate code to launch the app. e.g. @code class MyJUCEApp : public JUCEApplication { public: MyJUCEApp() {} ~MyJUCEApp() {} void initialise (const String& commandLine) override { myMainWindow = new MyApplicationWindow(); myMainWindow->setBounds (100, 100, 400, 500); myMainWindow->setVisible (true); } void shutdown() override { myMainWindow = nullptr; } const String getApplicationName() override { return "Super JUCE-o-matic"; } const String getApplicationVersion() override { return "1.0"; } private: ScopedPointer myMainWindow; }; // this generates boilerplate code to launch our app class: START_JUCE_APPLICATION (MyJUCEApp) @endcode @see JUCEApplication, START_JUCE_APPLICATION */ class JUCE_API JUCEApplicationBase { protected: //============================================================================== JUCEApplicationBase(); public: /** Destructor. */ virtual ~JUCEApplicationBase(); //============================================================================== /** Returns the global instance of the application object that's running. */ static JUCEApplicationBase* getInstance() noexcept { return appInstance; } //============================================================================== /** Returns the application's name. */ virtual const String getApplicationName() = 0; /** Returns the application's version number. */ virtual const String getApplicationVersion() = 0; /** Checks whether multiple instances of the app are allowed. If you application class returns true for this, more than one instance is permitted to run (except on the Mac where this isn't possible). If it's false, the second instance won't start, but it you will still get a callback to anotherInstanceStarted() to tell you about this - which gives you a chance to react to what the user was trying to do. */ virtual bool moreThanOneInstanceAllowed() = 0; /** Called when the application starts. This will be called once to let the application do whatever initialisation it needs, create its windows, etc. After the method returns, the normal event-dispatch loop will be run, until the quit() method is called, at which point the shutdown() method will be called to let the application clear up anything it needs to delete. If during the initialise() method, the application decides not to start-up after all, it can just call the quit() method and the event loop won't be run. @param commandLineParameters the line passed in does not include the name of the executable, just the parameter list. To get the parameters as an array, you can call JUCEApplication::getCommandLineParameters() @see shutdown, quit */ virtual void initialise (const String& commandLineParameters) = 0; /* Called to allow the application to clear up before exiting. After JUCEApplication::quit() has been called, the event-dispatch loop will terminate, and this method will get called to allow the app to sort itself out. Be careful that nothing happens in this method that might rely on messages being sent, or any kind of window activity, because the message loop is no longer running at this point. @see DeletedAtShutdown */ virtual void shutdown() = 0; /** Indicates that the user has tried to start up another instance of the app. This will get called even if moreThanOneInstanceAllowed() is false. */ virtual void anotherInstanceStarted (const String& commandLine) = 0; /** Called when the operating system is trying to close the application. The default implementation of this method is to call quit(), but it may be overloaded to ignore the request or do some other special behaviour instead. For example, you might want to offer the user the chance to save their changes before quitting, and give them the chance to cancel. If you want to send a quit signal to your app, this is the correct method to call, because it means that requests that come from the system get handled in the same way as those from your own application code. So e.g. you'd call this method from a "quit" item on a menu bar. */ virtual void systemRequestedQuit() = 0; /** This method is called when the application is being put into background mode by the operating system. */ virtual void suspended() = 0; /** This method is called when the application is being woken from background mode by the operating system. */ virtual void resumed() = 0; /** If any unhandled exceptions make it through to the message dispatch loop, this callback will be triggered, in case you want to log them or do some other type of error-handling. If the type of exception is derived from the std::exception class, the pointer passed-in will be valid. If the exception is of unknown type, this pointer will be null. */ virtual void unhandledException (const std::exception*, const String& sourceFilename, int lineNumber) = 0; //============================================================================== /** Signals that the main message loop should stop and the application should terminate. This isn't synchronous, it just posts a quit message to the main queue, and when this message arrives, the message loop will stop, the shutdown() method will be called, and the app will exit. Note that this will cause an unconditional quit to happen, so if you need an extra level before this, e.g. to give the user the chance to save their work and maybe cancel the quit, you'll need to handle this in the systemRequestedQuit() method - see that method's help for more info. @see MessageManager */ static void quit(); //============================================================================== /** Returns the application's command line parameters as a set of strings. @see getCommandLineParameters */ static StringArray JUCE_CALLTYPE getCommandLineParameterArray(); /** Returns the application's command line parameters as a single string. @see getCommandLineParameterArray */ static String JUCE_CALLTYPE getCommandLineParameters(); //============================================================================== /** Sets the value that should be returned as the application's exit code when the app quits. This is the value that's returned by the main() function. Normally you'd leave this as 0 unless you want to indicate an error code. @see getApplicationReturnValue */ void setApplicationReturnValue (int newReturnValue) noexcept; /** Returns the value that has been set as the application's exit code. @see setApplicationReturnValue */ int getApplicationReturnValue() const noexcept { return appReturnValue; } //============================================================================== /** Returns true if this executable is running as an app (as opposed to being a plugin or other kind of shared library. */ static bool isStandaloneApp() noexcept { return createInstance != nullptr; } /** Returns true if the application hasn't yet completed its initialise() method and entered the main event loop. This is handy for things like splash screens to know when the app's up-and-running properly. */ bool isInitialising() const noexcept { return stillInitialising; } //============================================================================== #ifndef DOXYGEN // The following methods are for internal use only... static int main(); static int main (int argc, const char* argv[]); static void appWillTerminateByForce(); typedef JUCEApplicationBase* (*CreateInstanceFunction)(); static CreateInstanceFunction createInstance; virtual bool initialiseApp(); int shutdownApp(); static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); bool sendCommandLineToPreexistingInstance(); #endif private: //============================================================================== static JUCEApplicationBase* appInstance; int appReturnValue; bool stillInitialising; struct MultipleInstanceHandler; friend struct MultipleInstanceHandler; friend struct ContainerDeletePolicy; ScopedPointer multipleInstanceHandler; JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase) }; #endif // JUCE_APPLICATIONBASE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h000066400000000000000000000053571320201440200321170ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_CALLBACKMESSAGE_H_INCLUDED #define JUCE_CALLBACKMESSAGE_H_INCLUDED //============================================================================== /** A message that invokes a callback method when it gets delivered. You can use this class to fire off actions that you want to be performed later on the message thread. To use it, create a subclass of CallbackMessage which implements the messageCallback() method, then call post() to dispatch it. The event thread will then invoke your messageCallback() method later on, and will automatically delete the message object afterwards. Always create a new instance of a CallbackMessage on the heap, as it will be deleted automatically after the message has been delivered. @see MessageManager, MessageListener, ActionListener, ChangeListener */ class JUCE_API CallbackMessage : public MessageManager::MessageBase { public: //============================================================================== CallbackMessage() noexcept {} /** Destructor. */ ~CallbackMessage() {} //============================================================================== /** Called when the message is delivered. You should implement this method and make it do whatever action you want to perform. Note that like all other messages, this object will be deleted immediately after this method has been invoked. */ virtual void messageCallback() = 0; private: // Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered // messages still in the system event queue. These aren't harmful, but can cause annoying assertions. JUCE_DECLARE_NON_COPYABLE (CallbackMessage) }; #endif // JUCE_CALLBACKMESSAGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp000066400000000000000000000050101320201440200330220ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ static SpinLock deletedAtShutdownLock; DeletedAtShutdown::DeletedAtShutdown() { const SpinLock::ScopedLockType sl (deletedAtShutdownLock); getObjects().add (this); } DeletedAtShutdown::~DeletedAtShutdown() { const SpinLock::ScopedLockType sl (deletedAtShutdownLock); getObjects().removeFirstMatchingValue (this); } void DeletedAtShutdown::deleteAll() { // make a local copy of the array, so it can't get into a loop if something // creates another DeletedAtShutdown object during its destructor. Array localCopy; { const SpinLock::ScopedLockType sl (deletedAtShutdownLock); localCopy = getObjects(); } for (int i = localCopy.size(); --i >= 0;) { JUCE_TRY { DeletedAtShutdown* deletee = localCopy.getUnchecked(i); // double-check that it's not already been deleted during another object's destructor. { const SpinLock::ScopedLockType sl (deletedAtShutdownLock); if (! getObjects().contains (deletee)) deletee = nullptr; } delete deletee; } JUCE_CATCH_EXCEPTION } // if no objects got re-created during shutdown, this should have been emptied by their // destructors jassert (getObjects().size() == 0); getObjects().clear(); // just to make sure the array doesn't have any memory still allocated } Array & DeletedAtShutdown::getObjects() { static Array objects; return objects; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h000066400000000000000000000043711320201440200325000ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DELETEDATSHUTDOWN_H_INCLUDED #define JUCE_DELETEDATSHUTDOWN_H_INCLUDED //============================================================================== /** Classes derived from this will be automatically deleted when the application exits. After JUCEApplicationBase::shutdown() has been called, any objects derived from DeletedAtShutdown which are still in existence will be deleted in the reverse order to that in which they were created. So if you've got a singleton and don't want to have to explicitly delete it, just inherit from this and it'll be taken care of. */ class JUCE_API DeletedAtShutdown { protected: /** Creates a DeletedAtShutdown object. */ DeletedAtShutdown(); /** Destructor. It's ok to delete these objects explicitly - it's only the ones left dangling at the end that will be deleted automatically. */ virtual ~DeletedAtShutdown(); public: /** Deletes all extant objects. This shouldn't be used by applications, as it's called automatically in the shutdown code of the JUCEApplicationBase class. */ static void deleteAll(); private: static Array & getObjects(); JUCE_DECLARE_NON_COPYABLE (DeletedAtShutdown) }; #endif // JUCE_DELETEDATSHUTDOWN_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h000066400000000000000000000105021320201440200320620ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_INITIALISATION_H_INCLUDED #define JUCE_INITIALISATION_H_INCLUDED //============================================================================== /** Initialises Juce's GUI classes. If you're embedding Juce into an application that uses its own event-loop rather than using the START_JUCE_APPLICATION macro, call this function before making any Juce calls, to make sure things are initialised correctly. Note that if you're creating a Juce DLL for Windows, you may also need to call the Process::setCurrentModuleInstanceHandle() method. @see shutdownJuce_GUI() */ JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI(); /** Clears up any static data being used by Juce's GUI classes. If you're embedding Juce into an application that uses its own event-loop rather than using the START_JUCE_APPLICATION macro, call this function in your shutdown code to clean up any juce objects that might be lying around. @see initialiseJuce_GUI() */ JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI(); //============================================================================== /** A utility object that helps you initialise and shutdown Juce correctly using an RAII pattern. When the first instance of this class is created, it calls initialiseJuce_GUI(), and when the last instance is deleted, it calls shutdownJuce_GUI(), so that you can easily be sure that as long as at least one instance of the class exists, the library will be initialised. This class is particularly handy to use at the beginning of a console app's main() function, because it'll take care of shutting down whenever you return from the main() call. Be careful with your threading though - to be safe, you should always make sure that these objects are created and deleted on the message thread. */ class JUCE_API ScopedJuceInitialiser_GUI { public: /** The constructor simply calls initialiseJuce_GUI(). */ ScopedJuceInitialiser_GUI(); /** The destructor simply calls shutdownJuce_GUI(). */ ~ScopedJuceInitialiser_GUI(); }; //============================================================================== /** To start a JUCE app, use this macro: START_JUCE_APPLICATION (AppSubClass) where AppSubClass is the name of a class derived from JUCEApplication or JUCEApplicationBase. See the JUCEApplication and JUCEApplicationBase class documentation for more details. */ #ifdef DOXYGEN #define START_JUCE_APPLICATION(AppClass) #elif JUCE_ANDROID #define START_JUCE_APPLICATION(AppClass) \ juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } #else #if JUCE_WINDOWS && ! defined (_CONSOLE) #define JUCE_MAIN_FUNCTION int __stdcall WinMain (struct HINSTANCE__*, struct HINSTANCE__*, char*, int) #define JUCE_MAIN_FUNCTION_ARGS #else #define JUCE_MAIN_FUNCTION int main (int argc, char* argv[]) #define JUCE_MAIN_FUNCTION_ARGS argc, (const char**) argv #endif #define START_JUCE_APPLICATION(AppClass) \ static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ extern "C" JUCE_MAIN_FUNCTION \ { \ juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; \ return juce::JUCEApplicationBase::main (JUCE_MAIN_FUNCTION_ARGS); \ } #endif #endif // JUCE_INITIALISATION_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_Message.h000066400000000000000000000045411320201440200304740ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MESSAGE_H_INCLUDED #define JUCE_MESSAGE_H_INCLUDED class MessageListener; //============================================================================== /** The base class for objects that can be sent to a MessageListener. If you want to send a message that carries some kind of custom data, just create a subclass of Message with some appropriate member variables to hold your data. Always create a new instance of a Message object on the heap, as it will be deleted automatically after the message has been delivered. @see MessageListener, MessageManager, ActionListener, ChangeListener */ class JUCE_API Message : public MessageManager::MessageBase { public: //============================================================================== /** Creates an uninitialised message. */ Message() noexcept; ~Message(); typedef ReferenceCountedObjectPtr Ptr; //============================================================================== private: friend class MessageListener; WeakReference recipient; void messageCallback() override; // Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered // messages still in the system event queue. These aren't harmful, but can cause annoying assertions. JUCE_DECLARE_NON_COPYABLE (Message) }; #endif // JUCE_MESSAGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp000066400000000000000000000030341320201440200325310ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ Message::Message() noexcept {} Message::~Message() {} void Message::messageCallback() { if (MessageListener* const r = recipient) r->handleMessage (*this); } MessageListener::MessageListener() noexcept { // Are you trying to create a messagelistener before or after juce has been intialised?? jassert (MessageManager::getInstanceWithoutCreating() != nullptr); } MessageListener::~MessageListener() { masterReference.clear(); } void MessageListener::postMessage (Message* const message) const { message->recipient = const_cast (this); message->post(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h000066400000000000000000000047601320201440200322050ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MESSAGELISTENER_H_INCLUDED #define JUCE_MESSAGELISTENER_H_INCLUDED //============================================================================== /** MessageListener subclasses can post and receive Message objects. @see Message, MessageManager, ActionListener, ChangeListener */ class JUCE_API MessageListener { public: //============================================================================== MessageListener() noexcept; /** Destructor. */ virtual ~MessageListener(); //============================================================================== /** This is the callback method that receives incoming messages. This is called by the MessageManager from its dispatch loop. @see postMessage */ virtual void handleMessage (const Message& message) = 0; //============================================================================== /** Sends a message to the message queue, for asynchronous delivery to this listener later on. This method can be called safely by any thread. @param message the message object to send - this will be deleted automatically by the message queue, so make sure it's allocated on the heap, not the stack! @see handleMessage */ void postMessage (Message* message) const; private: WeakReference::Master masterReference; friend class WeakReference; }; #endif // JUCE_MESSAGELISTENER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp000066400000000000000000000261201320201440200323170ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ MessageManager::MessageManager() noexcept : quitMessagePosted (false), quitMessageReceived (false), messageThreadId (Thread::getCurrentThreadId()), threadWithLock (0) { if (JUCEApplicationBase::isStandaloneApp()) Thread::setCurrentThreadName ("Juce Message Thread"); } MessageManager::~MessageManager() noexcept { broadcaster = nullptr; doPlatformSpecificShutdown(); jassert (instance == this); instance = nullptr; // do this last in case this instance is still needed by doPlatformSpecificShutdown() } MessageManager* MessageManager::instance = nullptr; MessageManager* MessageManager::getInstance() { if (instance == nullptr) { instance = new MessageManager(); doPlatformSpecificInitialisation(); } return instance; } MessageManager* MessageManager::getInstanceWithoutCreating() noexcept { return instance; } void MessageManager::deleteInstance() { deleteAndZero (instance); } //============================================================================== bool MessageManager::MessageBase::post() { MessageManager* const mm = MessageManager::instance; if (mm == nullptr || mm->quitMessagePosted || ! postMessageToSystemQueue (this)) { Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count) return false; } return true; } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS) bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { jassert (isThisTheMessageThread()); // must only be called by the message thread const int64 endTime = Time::currentTimeMillis() + millisecondsToRunFor; while (! quitMessageReceived) { JUCE_TRY { if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0)) Thread::sleep (1); } JUCE_CATCH_EXCEPTION if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime) break; } return ! quitMessageReceived; } #endif #if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID) class MessageManager::QuitMessage : public MessageManager::MessageBase { public: QuitMessage() {} void messageCallback() override { if (MessageManager* const mm = MessageManager::instance) mm->quitMessageReceived = true; } JUCE_DECLARE_NON_COPYABLE (QuitMessage) }; void MessageManager::runDispatchLoop() { jassert (isThisTheMessageThread()); // must only be called by the message thread while (! quitMessageReceived) { JUCE_TRY { if (! dispatchNextMessageOnSystemQueue (false)) Thread::sleep (1); } JUCE_CATCH_EXCEPTION } } void MessageManager::stopDispatchLoop() { (new QuitMessage())->post(); quitMessagePosted = true; } #endif //============================================================================== #if JUCE_COMPILER_SUPPORTS_LAMBDAS struct AsyncFunction : private MessageManager::MessageBase { AsyncFunction (std::function f) : fn (f) { post(); } private: std::function fn; void messageCallback() override { fn(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncFunction) }; void MessageManager::callAsync (std::function f) { new AsyncFunction (f); } #endif //============================================================================== class AsyncFunctionCallback : public MessageManager::MessageBase { public: AsyncFunctionCallback (MessageCallbackFunction* const f, void* const param) : result (nullptr), func (f), parameter (param) {} void messageCallback() override { result = (*func) (parameter); finished.signal(); } WaitableEvent finished; void* volatile result; private: MessageCallbackFunction* const func; void* const parameter; JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback) }; void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* const func, void* const parameter) { if (isThisTheMessageThread()) return func (parameter); // If this thread has the message manager locked, then this will deadlock! jassert (! currentThreadHasLockedMessageManager()); const ReferenceCountedObjectPtr message (new AsyncFunctionCallback (func, parameter)); if (message->post()) { message->finished.wait(); return message->result; } jassertfalse; // the OS message queue failed to send the message! return nullptr; } //============================================================================== void MessageManager::deliverBroadcastMessage (const String& value) { if (broadcaster != nullptr) broadcaster->sendActionMessage (value); } void MessageManager::registerBroadcastListener (ActionListener* const listener) { if (broadcaster == nullptr) broadcaster = new ActionBroadcaster(); broadcaster->addActionListener (listener); } void MessageManager::deregisterBroadcastListener (ActionListener* const listener) { if (broadcaster != nullptr) broadcaster->removeActionListener (listener); } //============================================================================== bool MessageManager::isThisTheMessageThread() const noexcept { return Thread::getCurrentThreadId() == messageThreadId; } void MessageManager::setCurrentThreadAsMessageThread() { const Thread::ThreadID thisThread = Thread::getCurrentThreadId(); if (messageThreadId != thisThread) { messageThreadId = thisThread; // This is needed on windows to make sure the message window is created by this thread doPlatformSpecificShutdown(); doPlatformSpecificInitialisation(); } } bool MessageManager::currentThreadHasLockedMessageManager() const noexcept { const Thread::ThreadID thisThread = Thread::getCurrentThreadId(); return thisThread == messageThreadId || thisThread == threadWithLock; } //============================================================================== //============================================================================== /* The only safe way to lock the message thread while another thread does some work is by posting a special message, whose purpose is to tie up the event loop until the other thread has finished its business. Any other approach can get horribly deadlocked if the OS uses its own hidden locks which get locked before making an event callback, because if the same OS lock gets indirectly accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens in Cocoa). */ class MessageManagerLock::BlockingMessage : public MessageManager::MessageBase { public: BlockingMessage() noexcept {} void messageCallback() override { lockedEvent.signal(); releaseEvent.wait(); } WaitableEvent lockedEvent, releaseEvent; JUCE_DECLARE_NON_COPYABLE (BlockingMessage) }; //============================================================================== MessageManagerLock::MessageManagerLock (Thread* const threadToCheck) : blockingMessage(), locked (attemptLock (threadToCheck, nullptr)) { } MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) : blockingMessage(), locked (attemptLock (nullptr, jobToCheckForExitSignal)) { } bool MessageManagerLock::attemptLock (Thread* const threadToCheck, ThreadPoolJob* const job) { MessageManager* const mm = MessageManager::instance; if (mm == nullptr) return false; if (mm->currentThreadHasLockedMessageManager()) return true; if (threadToCheck == nullptr && job == nullptr) { mm->lockingLock.enter(); } else { while (! mm->lockingLock.tryEnter()) { if ((threadToCheck != nullptr && threadToCheck->threadShouldExit()) || (job != nullptr && job->shouldExit())) return false; Thread::yield(); } } blockingMessage = new BlockingMessage(); if (! blockingMessage->post()) { blockingMessage = nullptr; return false; } while (! blockingMessage->lockedEvent.wait (20)) { if ((threadToCheck != nullptr && threadToCheck->threadShouldExit()) || (job != nullptr && job->shouldExit())) { blockingMessage->releaseEvent.signal(); blockingMessage = nullptr; mm->lockingLock.exit(); return false; } } jassert (mm->threadWithLock == 0); mm->threadWithLock = Thread::getCurrentThreadId(); return true; } MessageManagerLock::~MessageManagerLock() noexcept { if (blockingMessage != nullptr) { MessageManager* const mm = MessageManager::instance; jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager()); blockingMessage->releaseEvent.signal(); blockingMessage = nullptr; if (mm != nullptr) { mm->threadWithLock = 0; mm->lockingLock.exit(); } } } //============================================================================== JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI(); JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI() { JUCE_AUTORELEASEPOOL { MessageManager::getInstance(); } } JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI(); JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI() { JUCE_AUTORELEASEPOOL { DeletedAtShutdown::deleteAll(); MessageManager::deleteInstance(); } } static int numScopedInitInstances = 0; ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); } ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h000066400000000000000000000317361320201440200317750ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MESSAGEMANAGER_H_INCLUDED #define JUCE_MESSAGEMANAGER_H_INCLUDED class MessageManagerLock; class ThreadPoolJob; class ActionListener; class ActionBroadcaster; //============================================================================== /** See MessageManager::callFunctionOnMessageThread() for use of this function type */ typedef void* (MessageCallbackFunction) (void* userData); //============================================================================== /** This class is in charge of the application's event-dispatch loop. @see Message, CallbackMessage, MessageManagerLock, JUCEApplication, JUCEApplicationBase */ class JUCE_API MessageManager { public: //============================================================================== /** Returns the global instance of the MessageManager. */ static MessageManager* getInstance(); /** Returns the global instance of the MessageManager, or nullptr if it doesn't exist. */ static MessageManager* getInstanceWithoutCreating() noexcept; /** Deletes the global MessageManager instance. Does nothing if no instance had been created. */ static void deleteInstance(); //============================================================================== /** Runs the event dispatch loop until a stop message is posted. This method is only intended to be run by the application's startup routine, as it blocks, and will only return after the stopDispatchLoop() method has been used. @see stopDispatchLoop */ void runDispatchLoop(); /** Sends a signal that the dispatch loop should terminate. After this is called, the runDispatchLoop() or runDispatchLoopUntil() methods will be interrupted and will return. @see runDispatchLoop */ void stopDispatchLoop(); /** Returns true if the stopDispatchLoop() method has been called. */ bool hasStopMessageBeenSent() const noexcept { return quitMessagePosted; } #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN /** Synchronously dispatches messages until a given time has elapsed. Returns false if a quit message has been posted by a call to stopDispatchLoop(), otherwise returns true. */ bool runDispatchLoopUntil (int millisecondsToRunFor); #endif //============================================================================== #if JUCE_COMPILER_SUPPORTS_LAMBDAS /** Asynchronously invokes a function or C++11 lambda on the message thread. Internally this uses the CallbackMessage class to invoke the callback. */ static void callAsync (std::function); #endif /** Calls a function using the message-thread. This can be used by any thread to cause this function to be called-back by the message thread. If it's the message-thread that's calling this method, then the function will just be called; if another thread is calling, a message will be posted to the queue, and this method will block until that message is delivered, the function is called, and the result is returned. Be careful not to cause any deadlocks with this! It's easy to do - e.g. if the caller thread has a critical section locked, which an unrelated message callback then tries to lock before the message thread gets round to processing this callback. @param callback the function to call - its signature must be @code void* myCallbackFunction (void*) @endcode @param userData a user-defined pointer that will be passed to the function that gets called @returns the value that the callback function returns. @see MessageManagerLock */ void* callFunctionOnMessageThread (MessageCallbackFunction* callback, void* userData); /** Returns true if the caller-thread is the message thread. */ bool isThisTheMessageThread() const noexcept; /** Called to tell the manager that the current thread is the one that's running the dispatch loop. (Best to ignore this method unless you really know what you're doing..) @see getCurrentMessageThread */ void setCurrentThreadAsMessageThread(); /** Returns the ID of the current message thread, as set by setCurrentThreadAsMessageThread(). (Best to ignore this method unless you really know what you're doing..) @see setCurrentThreadAsMessageThread */ Thread::ThreadID getCurrentMessageThread() const noexcept { return messageThreadId; } /** Returns true if the caller thread has currently got the message manager locked. see the MessageManagerLock class for more info about this. This will be true if the caller is the message thread, because that automatically gains a lock while a message is being dispatched. */ bool currentThreadHasLockedMessageManager() const noexcept; //============================================================================== /** Sends a message to all other JUCE applications that are running. @param messageText the string that will be passed to the actionListenerCallback() method of the broadcast listeners in the other app. @see registerBroadcastListener, ActionListener */ static void broadcastMessage (const String& messageText); /** Registers a listener to get told about broadcast messages. The actionListenerCallback() callback's string parameter is the message passed into broadcastMessage(). @see broadcastMessage */ void registerBroadcastListener (ActionListener* listener); /** Deregisters a broadcast listener. */ void deregisterBroadcastListener (ActionListener* listener); //============================================================================== /** Internal class used as the base class for all message objects. You shouldn't need to use this directly - see the CallbackMessage or Message classes instead. */ class JUCE_API MessageBase : public ReferenceCountedObject { public: MessageBase() noexcept {} virtual ~MessageBase() {} virtual void messageCallback() = 0; bool post(); typedef ReferenceCountedObjectPtr Ptr; JUCE_DECLARE_NON_COPYABLE (MessageBase) }; //============================================================================== #ifndef DOXYGEN // Internal methods - do not use! void deliverBroadcastMessage (const String&); ~MessageManager() noexcept; #endif private: //============================================================================== MessageManager() noexcept; static MessageManager* instance; friend class MessageBase; class QuitMessage; friend class QuitMessage; friend class MessageManagerLock; ScopedPointer broadcaster; bool quitMessagePosted, quitMessageReceived; Thread::ThreadID messageThreadId; Thread::ThreadID volatile threadWithLock; CriticalSection lockingLock; static bool postMessageToSystemQueue (MessageBase*); static void* exitModalLoopCallback (void*); static void doPlatformSpecificInitialisation(); static void doPlatformSpecificShutdown(); static bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager) }; //============================================================================== /** Used to make sure that the calling thread has exclusive access to the message loop. Because it's not thread-safe to call any of the Component or other UI classes from threads other than the message thread, one of these objects can be used to lock the message loop and allow this to be done. The message thread will be suspended for the lifetime of the MessageManagerLock object, so create one on the stack like this: @code void MyThread::run() { someData = 1234; const MessageManagerLock mmLock; // the event loop will now be locked so it's safe to make a few calls.. myComponent->setBounds (newBounds); myComponent->repaint(); // ..the event loop will now be unlocked as the MessageManagerLock goes out of scope } @endcode Obviously be careful not to create one of these and leave it lying around, or your app will grind to a halt! Another caveat is that using this in conjunction with other CriticalSections can create lots of interesting ways of producing a deadlock! In particular, if your message thread calls stopThread() for a thread that uses these locks, you'll get an (occasional) deadlock.. @see MessageManager, MessageManager::currentThreadHasLockedMessageManager */ class JUCE_API MessageManagerLock { public: //============================================================================== /** Tries to acquire a lock on the message manager. The constructor attempts to gain a lock on the message loop, and the lock will be kept for the lifetime of this object. Optionally, you can pass a thread object here, and while waiting to obtain the lock, this method will keep checking whether the thread has been given the Thread::signalThreadShouldExit() signal. If this happens, then it will return without gaining the lock. If you pass a thread, you must check whether the lock was successful by calling lockWasGained(). If this is false, your thread is being told to die, so you should take evasive action. If you pass nullptr for the thread object, it will wait indefinitely for the lock - be careful when doing this, because it's very easy to deadlock if your message thread attempts to call stopThread() on a thread just as that thread attempts to get the message lock. If the calling thread already has the lock, nothing will be done, so it's safe and quick to use these locks recursively. E.g. @code void run() { ... while (! threadShouldExit()) { MessageManagerLock mml (Thread::getCurrentThread()); if (! mml.lockWasGained()) return; // another thread is trying to kill us! ..do some locked stuff here.. } ..and now the MM is now unlocked.. } @endcode */ MessageManagerLock (Thread* threadToCheckForExitSignal = nullptr); //============================================================================== /** This has the same behaviour as the other constructor, but takes a ThreadPoolJob instead of a thread. See the MessageManagerLock (Thread*) constructor for details on how this works. */ MessageManagerLock (ThreadPoolJob* jobToCheckForExitSignal); //============================================================================== /** Releases the current thread's lock on the message manager. Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ ~MessageManagerLock() noexcept; //============================================================================== /** Returns true if the lock was successfully acquired. (See the constructor that takes a Thread for more info). */ bool lockWasGained() const noexcept { return locked; } private: class BlockingMessage; friend class ReferenceCountedObjectPtr; ReferenceCountedObjectPtr blockingMessage; bool locked; bool attemptLock (Thread*, ThreadPoolJob*); JUCE_DECLARE_NON_COPYABLE (MessageManagerLock) }; #endif // JUCE_MESSAGEMANAGER_H_INCLUDED juce_MountedVolumeListChangeDetector.h000066400000000000000000000037161320201440200352730ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MOUNTEDVOLUMELISTCHANGEDETECTOR_H_INCLUDED #define JUCE_MOUNTEDVOLUMELISTCHANGEDETECTOR_H_INCLUDED #if JUCE_MAC || JUCE_WINDOWS || defined (DOXYGEN) //============================================================================== /** An instance of this class will provide callbacks when drives are mounted or unmounted on the system. Just inherit from this class and implement the pure virtual method to get the callbacks, there's no need to do anything else. @see File::findFileSystemRoots() */ class JUCE_API MountedVolumeListChangeDetector { public: MountedVolumeListChangeDetector(); virtual ~MountedVolumeListChangeDetector(); /** This method is called when a volume is mounted or unmounted. */ virtual void mountedVolumeListChanged() = 0; private: JUCE_PUBLIC_IN_DLL_BUILD (struct Pimpl) friend struct ContainerDeletePolicy; ScopedPointer pimpl; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MountedVolumeListChangeDetector) }; #endif #endif // JUCE_MOUNTEDVOLUMELISTCHANGEDETECTOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h000066400000000000000000000031471320201440200324010ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_NOTIFICATIONTYPE_H_INCLUDED #define JUCE_NOTIFICATIONTYPE_H_INCLUDED //============================================================================== /** These enums are used in various classes to indicate whether a notification event should be sent out. */ enum NotificationType { dontSendNotification = 0, /**< No notification message should be sent. */ sendNotification = 1, /**< Requests a notification message, either synchronous or not. */ sendNotificationSync, /**< Requests a synchronous notification. */ sendNotificationAsync, /**< Requests an asynchronous notification. */ }; #endif // JUCE_NOTIFICATIONTYPE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/000077500000000000000000000000001320201440200254045ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_ScopedXLock.h000066400000000000000000000031611320201440200307420ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_SCOPEDXLOCK_H_INCLUDED #define JUCE_SCOPEDXLOCK_H_INCLUDED //============================================================================== #if JUCE_LINUX || DOXYGEN /** A handy class that uses XLockDisplay and XUnlockDisplay to lock the X server using RAII (Only available in Linux!). */ class ScopedXLock { public: /** Creating a ScopedXLock object locks the X display. This uses XLockDisplay() to grab the display that Juce is using. */ ScopedXLock(); /** Deleting a ScopedXLock object unlocks the X display. This calls XUnlockDisplay() to release the lock. */ ~ScopedXLock(); }; #endif #endif // JUCE_SCOPEDXLOCK_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp000066400000000000000000000050611320201440200325350ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ void MessageManager::doPlatformSpecificInitialisation() {} void MessageManager::doPlatformSpecificShutdown() {} //============================================================================== bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages) { Logger::outputDebugString ("*** Modal loops are not possible in Android!! Exiting..."); exit (1); return true; } //============================================================================== bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { message->incReferenceCount(); android.activity.callVoidMethod (JuceAppActivity.postMessage, (jlong) (pointer_sized_uint) message); return true; } JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, deliverMessage, void, (JNIEnv* env, jobject activity, jlong value)) { JUCE_TRY { MessageManager::MessageBase* const message = (MessageManager::MessageBase*) (pointer_sized_uint) value; message->messageCallback(); message->decReferenceCount(); } JUCE_CATCH_EXCEPTION } //============================================================================== void MessageManager::broadcastMessage (const String&) { } void MessageManager::runDispatchLoop() { } void MessageManager::stopDispatchLoop() { struct QuitCallback : public CallbackMessage { QuitCallback() {} void messageCallback() override { android.activity.callVoidMethod (JuceAppActivity.finish); } }; (new QuitCallback())->post(); quitMessagePosted = true; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm000066400000000000000000000057761320201440200325150ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ void MessageManager::runDispatchLoop() { jassert (isThisTheMessageThread()); // must only be called by the message thread while (! quitMessagePosted) { JUCE_AUTORELEASEPOOL { [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]]; } } } void MessageManager::stopDispatchLoop() { [[[UIApplication sharedApplication] delegate] applicationWillTerminate: [UIApplication sharedApplication]]; exit (0); // iOS apps get no mercy.. } #if JUCE_MODAL_LOOPS_PERMITTED bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { JUCE_AUTORELEASEPOOL { jassert (isThisTheMessageThread()); // must only be called by the message thread uint32 startTime = Time::getMillisecondCounter(); NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001]; while (! quitMessagePosted) { JUCE_AUTORELEASEPOOL { [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: endDate]; if (millisecondsToRunFor >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) millisecondsToRunFor) break; } } return ! quitMessagePosted; } } #endif //============================================================================== static ScopedPointer messageQueue; void MessageManager::doPlatformSpecificInitialisation() { if (messageQueue == nullptr) messageQueue = new MessageQueue(); } void MessageManager::doPlatformSpecificShutdown() { messageQueue = nullptr; } bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { if (messageQueue != nullptr) messageQueue->post (message); return true; } void MessageManager::broadcastMessage (const String&) { // N/A on current iOS } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp000066400000000000000000000275101320201440200322570ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS) #define JUCE_DEBUG_XERRORS 1 #endif Display* display = nullptr; Window juce_messageWindowHandle = None; XContext windowHandleXContext; // This is referenced from Windowing.cpp typedef bool (*WindowMessageReceiveCallback) (XEvent&); WindowMessageReceiveCallback dispatchWindowMessage = nullptr; typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&); SelectionRequestCallback handleSelectionRequest = nullptr; //============================================================================== ScopedXLock::ScopedXLock() { if (display != nullptr) XLockDisplay (display); } ScopedXLock::~ScopedXLock() { if (display != nullptr) XUnlockDisplay (display); } //============================================================================== class InternalMessageQueue { public: InternalMessageQueue() : bytesInSocket (0), totalEventCount (0) { int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); (void) ret; jassert (ret == 0); } ~InternalMessageQueue() { close (fd[0]); close (fd[1]); clearSingletonInstance(); } //============================================================================== void postMessage (MessageManager::MessageBase* const msg) { const int maxBytesInSocketQueue = 128; ScopedLock sl (lock); queue.add (msg); if (bytesInSocket < maxBytesInSocketQueue) { ++bytesInSocket; ScopedUnlock ul (lock); const unsigned char x = 0xff; ssize_t bytesWritten = write (fd[0], &x, 1); (void) bytesWritten; } } bool isEmpty() const { ScopedLock sl (lock); return queue.size() == 0; } bool dispatchNextEvent() { // This alternates between giving priority to XEvents or internal messages, // to keep everything running smoothly.. if ((++totalEventCount & 1) != 0) return dispatchNextXEvent() || dispatchNextInternalMessage(); return dispatchNextInternalMessage() || dispatchNextXEvent(); } // Wait for an event (either XEvent, or an internal Message) bool sleepUntilEvent (const int timeoutMs) { if (! isEmpty()) return true; if (display != nullptr) { ScopedXLock xlock; if (XPending (display)) return true; } struct timeval tv; tv.tv_sec = 0; tv.tv_usec = timeoutMs * 1000; int fd0 = getWaitHandle(); int fdmax = fd0; fd_set readset; FD_ZERO (&readset); FD_SET (fd0, &readset); if (display != nullptr) { ScopedXLock xlock; int fd1 = XConnectionNumber (display); FD_SET (fd1, &readset); fdmax = jmax (fd0, fd1); } const int ret = select (fdmax + 1, &readset, 0, 0, &tv); return (ret > 0); // ret <= 0 if error or timeout } //============================================================================== juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue) private: CriticalSection lock; ReferenceCountedArray queue; int fd[2]; int bytesInSocket; int totalEventCount; int getWaitHandle() const noexcept { return fd[1]; } static bool setNonBlocking (int handle) { int socketFlags = fcntl (handle, F_GETFL, 0); if (socketFlags == -1) return false; socketFlags |= O_NONBLOCK; return fcntl (handle, F_SETFL, socketFlags) == 0; } static bool dispatchNextXEvent() { if (display == nullptr) return false; XEvent evt; { ScopedXLock xlock; if (! XPending (display)) return false; XNextEvent (display, &evt); } if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle && handleSelectionRequest != nullptr) handleSelectionRequest (evt.xselectionrequest); else if (evt.xany.window != juce_messageWindowHandle && dispatchWindowMessage != nullptr) dispatchWindowMessage (evt); return true; } MessageManager::MessageBase::Ptr popNextMessage() { const ScopedLock sl (lock); if (bytesInSocket > 0) { --bytesInSocket; const ScopedUnlock ul (lock); unsigned char x; ssize_t numBytes = read (fd[1], &x, 1); (void) numBytes; } return queue.removeAndReturn (0); } bool dispatchNextInternalMessage() { if (const MessageManager::MessageBase::Ptr msg = popNextMessage()) { JUCE_TRY { msg->messageCallback(); return true; } JUCE_CATCH_EXCEPTION } return false; } }; juce_ImplementSingleton_SingleThreaded (InternalMessageQueue) //============================================================================== namespace LinuxErrorHandling { static bool errorOccurred = false; static bool keyboardBreakOccurred = false; static XErrorHandler oldErrorHandler = (XErrorHandler) 0; static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0; //============================================================================== // Usually happens when client-server connection is broken int ioErrorHandler (Display*) { DBG ("ERROR: connection to X server broken.. terminating."); if (JUCEApplicationBase::isStandaloneApp()) MessageManager::getInstance()->stopDispatchLoop(); errorOccurred = true; return 0; } int errorHandler (Display* display, XErrorEvent* event) { (void) display; (void) event; #if JUCE_DEBUG_XERRORS char errorStr[64] = { 0 }; char requestStr[64] = { 0 }; XGetErrorText (display, event->error_code, errorStr, 64); XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64); DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr); #endif return 0; } void installXErrorHandlers() { oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler); oldErrorHandler = XSetErrorHandler (errorHandler); } void removeXErrorHandlers() { if (JUCEApplicationBase::isStandaloneApp()) { XSetIOErrorHandler (oldIOErrorHandler); oldIOErrorHandler = 0; XSetErrorHandler (oldErrorHandler); oldErrorHandler = 0; } } //============================================================================== void keyboardBreakSignalHandler (int sig) { if (sig == SIGINT) keyboardBreakOccurred = true; } void installKeyboardBreakHandler() { struct sigaction saction; sigset_t maskSet; sigemptyset (&maskSet); saction.sa_handler = keyboardBreakSignalHandler; saction.sa_mask = maskSet; saction.sa_flags = 0; sigaction (SIGINT, &saction, 0); } } //============================================================================== void MessageManager::doPlatformSpecificInitialisation() { if (JUCEApplicationBase::isStandaloneApp()) { // Initialise xlib for multiple thread support static bool initThreadCalled = false; if (! initThreadCalled) { if (! XInitThreads()) { // This is fatal! Print error and closedown Logger::outputDebugString ("Failed to initialise xlib thread support."); Process::terminate(); return; } initThreadCalled = true; } LinuxErrorHandling::installXErrorHandlers(); LinuxErrorHandling::installKeyboardBreakHandler(); } // Create the internal message queue InternalMessageQueue::getInstance(); // Try to connect to a display String displayName (getenv ("DISPLAY")); if (displayName.isEmpty()) displayName = ":0.0"; display = XOpenDisplay (displayName.toUTF8()); if (display != nullptr) // This is not fatal! we can run headless. { // Create a context to store user data associated with Windows we create windowHandleXContext = XUniqueContext(); // We're only interested in client messages for this window, which are always sent XSetWindowAttributes swa; swa.event_mask = NoEventMask; // Create our message window (this will never be mapped) const int screen = DefaultScreen (display); juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen), 0, 0, 1, 1, 0, 0, InputOnly, DefaultVisual (display, screen), CWEventMask, &swa); } } void MessageManager::doPlatformSpecificShutdown() { InternalMessageQueue::deleteInstance(); if (display != nullptr && ! LinuxErrorHandling::errorOccurred) { XDestroyWindow (display, juce_messageWindowHandle); XCloseDisplay (display); juce_messageWindowHandle = 0; display = nullptr; LinuxErrorHandling::removeXErrorHandlers(); } } bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { if (LinuxErrorHandling::errorOccurred) return false; InternalMessageQueue::getInstanceWithoutCreating()->postMessage (message); return true; } void MessageManager::broadcastMessage (const String& /* value */) { /* TODO */ } // this function expects that it will NEVER be called simultaneously for two concurrent threads bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) { while (! LinuxErrorHandling::errorOccurred) { if (LinuxErrorHandling::keyboardBreakOccurred) { LinuxErrorHandling::errorOccurred = true; if (JUCEApplicationBase::isStandaloneApp()) Process::terminate(); break; } InternalMessageQueue* const queue = InternalMessageQueue::getInstanceWithoutCreating(); jassert (queue != nullptr); if (queue->dispatchNextEvent()) return true; if (returnIfNoPendingMessages) break; queue->sleepUntilEvent (2000); } return false; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm000066400000000000000000000404471320201440200324550ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ typedef void (*AppFocusChangeCallback)(); AppFocusChangeCallback appFocusChangeCallback = nullptr; typedef bool (*CheckEventBlockedByModalComps) (NSEvent*); CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr; typedef void (*MenuTrackingChangedCallback)(bool); MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr; //============================================================================== struct AppDelegate { public: AppDelegate() { static AppDelegateClass cls; delegate = [cls.createInstance() init]; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center addObserver: delegate selector: @selector (mainMenuTrackingBegan:) name: NSMenuDidBeginTrackingNotification object: nil]; [center addObserver: delegate selector: @selector (mainMenuTrackingEnded:) name: NSMenuDidEndTrackingNotification object: nil]; if (JUCEApplicationBase::isStandaloneApp()) { [NSApp setDelegate: delegate]; [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate selector: @selector (broadcastMessageCallback:) name: getBroadcastEventName() object: nil]; } else { [center addObserver: delegate selector: @selector (applicationDidResignActive:) name: NSApplicationDidResignActiveNotification object: NSApp]; [center addObserver: delegate selector: @selector (applicationDidBecomeActive:) name: NSApplicationDidBecomeActiveNotification object: NSApp]; [center addObserver: delegate selector: @selector (applicationWillUnhide:) name: NSApplicationWillUnhideNotification object: NSApp]; } } ~AppDelegate() { [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate]; [[NSNotificationCenter defaultCenter] removeObserver: delegate]; if (JUCEApplicationBase::isStandaloneApp()) { [NSApp setDelegate: nil]; [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate name: getBroadcastEventName() object: nil]; } [delegate release]; } static NSString* getBroadcastEventName() { return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64())); } MessageQueue messageQueue; id delegate; private: //============================================================================== struct AppDelegateClass : public ObjCClass { AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") { addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@@"); addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@"); addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); addMethod (@selector (dummyMethod), dummyMethod, "v@:"); registerClass(); } private: static void applicationWillFinishLaunching (id self, SEL, NSApplication*, NSNotification*) { [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self andSelector: @selector (getUrl:withReplyEvent:) forEventClass: kInternetEventClass andEventID: kAEGetURL]; } static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) { app->systemRequestedQuit(); if (! MessageManager::getInstance()->hasStopMessageBeenSent()) return NSTerminateCancel; } return NSTerminateNow; } static void applicationWillTerminate (id /*self*/, SEL, NSNotification*) { JUCEApplicationBase::appWillTerminateByForce(); } static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename) { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) { app->anotherInstanceStarted (quotedIfContainsSpaces (filename)); return YES; } return NO; } static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames) { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) { StringArray files; for (NSString* f in filenames) files.add (quotedIfContainsSpaces (f)); if (files.size() > 0) app->anotherInstanceStarted (files.joinIntoString (" ")); } } static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); } static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n) { NSDictionary* dict = (NSDictionary*) [n userInfo]; const String messageString (nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")])); MessageManager::getInstance()->deliverBroadcastMessage (messageString); } static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*) { if (menuTrackingChangedCallback != nullptr) (*menuTrackingChangedCallback) (true); } static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*) { if (menuTrackingChangedCallback != nullptr) (*menuTrackingChangedCallback) (false); } static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread) static void focusChanged() { if (appFocusChangeCallback != nullptr) (*appFocusChangeCallback)(); } static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*) { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue])); } static String quotedIfContainsSpaces (NSString* file) { String s (nsStringToJuce (file)); if (s.containsChar (' ')) s = s.quoted ('"'); return s; } }; }; //============================================================================== void MessageManager::runDispatchLoop() { if (! quitMessagePosted) // check that the quit message wasn't already posted.. { JUCE_AUTORELEASEPOOL { // must only be called by the message thread! jassert (isThisTheMessageThread()); #if JUCE_PROJUCER_LIVE_BUILD runDispatchLoopUntil (std::numeric_limits::max()); #else #if JUCE_CATCH_UNHANDLED_EXCEPTIONS @try { [NSApp run]; } @catch (NSException* e) { // An AppKit exception will kill the app, but at least this provides a chance to log it., std::runtime_error ex (std::string ("NSException: ") + [[e name] UTF8String] + ", Reason:" + [[e reason] UTF8String]); JUCEApplication::sendUnhandledException (&ex, __FILE__, __LINE__); } @finally { } #else [NSApp run]; #endif #endif } } } static void shutdownNSApp() { [NSApp stop: nil]; [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated) [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; } void MessageManager::stopDispatchLoop() { #if JUCE_PROJUCER_LIVE_BUILD quitMessagePosted = true; #else if (isThisTheMessageThread()) { quitMessagePosted = true; shutdownNSApp(); } else { struct QuitCallback : public CallbackMessage { QuitCallback() {} void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); } }; (new QuitCallback())->post(); } #endif } #if JUCE_MODAL_LOOPS_PERMITTED bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { jassert (millisecondsToRunFor >= 0); jassert (isThisTheMessageThread()); // must only be called by the message thread uint32 endTime = Time::getMillisecondCounter() + (uint32) millisecondsToRunFor; while (! quitMessagePosted) { JUCE_AUTORELEASEPOOL { CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true); NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001] inMode: NSDefaultRunLoopMode dequeue: YES]; if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e))) [NSApp sendEvent: e]; if (Time::getMillisecondCounter() >= endTime) break; } } return ! quitMessagePosted; } #endif //============================================================================== void initialiseNSApplication(); void initialiseNSApplication() { JUCE_AUTORELEASEPOOL { [NSApplication sharedApplication]; } } static AppDelegate* appDelegate = nullptr; void MessageManager::doPlatformSpecificInitialisation() { if (appDelegate == nil) appDelegate = new AppDelegate(); #if ! (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) // This launches a dummy thread, which forces Cocoa to initialise NSThreads correctly (needed prior to 10.5) if (! [NSThread isMultiThreaded]) [NSThread detachNewThreadSelector: @selector (dummyMethod) toTarget: appDelegate->delegate withObject: nil]; #endif } void MessageManager::doPlatformSpecificShutdown() { delete appDelegate; appDelegate = nullptr; } bool MessageManager::postMessageToSystemQueue (MessageBase* message) { jassert (appDelegate != nil); appDelegate->messageQueue.post (message); return true; } void MessageManager::broadcastMessage (const String& message) { NSDictionary* info = [NSDictionary dictionaryWithObject: juceStringToNS (message) forKey: nsStringLiteral ("message")]; [[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegate::getBroadcastEventName() object: nil userInfo: info]; } // Special function used by some plugin classes to re-post carbon events void repostCurrentNSEvent(); void repostCurrentNSEvent() { struct EventReposter : public CallbackMessage { EventReposter() : e ([[NSApp currentEvent] retain]) {} ~EventReposter() { [e release]; } void messageCallback() override { [NSApp postEvent: e atStart: YES]; } NSEvent* e; }; (new EventReposter())->post(); } //============================================================================== #if JUCE_MAC struct MountedVolumeListChangeDetector::Pimpl { Pimpl (MountedVolumeListChangeDetector& d) : owner (d) { static ObserverClass cls; delegate = [cls.createInstance() init]; ObserverClass::setOwner (delegate, this); NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter]; [nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidMountNotification object: nil]; [nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidUnmountNotification object: nil]; } ~Pimpl() { [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: delegate]; [delegate release]; } private: MountedVolumeListChangeDetector& owner; id delegate; struct ObserverClass : public ObjCClass { ObserverClass() : ObjCClass ("JUCEDriveObserver_") { addIvar ("owner"); addMethod (@selector (changed:), changed, "v@:@"); addProtocol (@protocol (NSTextInput)); registerClass(); } static Pimpl* getOwner (id self) { return getIvar (self, "owner"); } static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); } static void changed (id self, SEL, NSNotification*) { getOwner (self)->owner.mountedVolumeListChanged(); } }; }; MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); } MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {} #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h000066400000000000000000000062361320201440200320540ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_OSX_MESSAGEQUEUE_H_INCLUDED #define JUCE_OSX_MESSAGEQUEUE_H_INCLUDED //============================================================================== /* An internal message pump class used in OSX and iOS. */ class MessageQueue { public: MessageQueue() { #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 && ! JUCE_IOS runLoop = CFRunLoopGetMain(); #else runLoop = CFRunLoopGetCurrent(); #endif CFRunLoopSourceContext sourceContext; zerostruct (sourceContext); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) sourceContext.info = this; sourceContext.perform = runLoopSourceCallback; runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); } ~MessageQueue() { CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); CFRunLoopSourceInvalidate (runLoopSource); CFRelease (runLoopSource); } void post (MessageManager::MessageBase* const message) { messages.add (message); CFRunLoopSourceSignal (runLoopSource); CFRunLoopWakeUp (runLoop); } private: ReferenceCountedArray messages; CFRunLoopRef runLoop; CFRunLoopSourceRef runLoopSource; bool deliverNextMessage() { const MessageManager::MessageBase::Ptr nextMessage (messages.removeAndReturn (0)); if (nextMessage == nullptr) return false; JUCE_AUTORELEASEPOOL { JUCE_TRY { nextMessage->messageCallback(); } JUCE_CATCH_EXCEPTION } return true; } void runLoopCallback() { for (int i = 4; --i >= 0;) if (! deliverNextMessage()) return; CFRunLoopSourceSignal (runLoopSource); CFRunLoopWakeUp (runLoop); } static void runLoopSourceCallback (void* info) { static_cast (info)->runLoopCallback(); } }; #endif // JUCE_OSX_MESSAGEQUEUE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h000066400000000000000000000106711320201440200335020ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED #define JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED //============================================================================== class HiddenMessageWindow { public: HiddenMessageWindow (const TCHAR* const messageWindowName, WNDPROC wndProc) { String className ("JUCE_"); className << String::toHexString (Time::getHighResolutionTicks()); HMODULE moduleHandle = (HMODULE) Process::getCurrentModuleInstanceHandle(); WNDCLASSEX wc = { 0 }; wc.cbSize = sizeof (wc); wc.lpfnWndProc = wndProc; wc.cbWndExtra = 4; wc.hInstance = moduleHandle; wc.lpszClassName = className.toWideCharPointer(); atom = RegisterClassEx (&wc); jassert (atom != 0); hwnd = CreateWindow (getClassNameFromAtom(), messageWindowName, 0, 0, 0, 0, 0, 0, 0, moduleHandle, 0); jassert (hwnd != 0); } ~HiddenMessageWindow() { DestroyWindow (hwnd); UnregisterClass (getClassNameFromAtom(), 0); } inline HWND getHWND() const noexcept { return hwnd; } private: ATOM atom; HWND hwnd; LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) MAKELONG (atom, 0); } }; //============================================================================== class JuceWindowIdentifier { public: static bool isJUCEWindow (HWND hwnd) noexcept { return GetWindowLongPtr (hwnd, GWLP_USERDATA) == getImprobableWindowNumber(); } static void setAsJUCEWindow (HWND hwnd, bool isJuceWindow) noexcept { SetWindowLongPtr (hwnd, GWLP_USERDATA, isJuceWindow ? getImprobableWindowNumber() : 0); } private: static LONG_PTR getImprobableWindowNumber() noexcept { static LONG_PTR number = (LONG_PTR) Random::getSystemRandom().nextInt64(); return number; } }; //============================================================================== class DeviceChangeDetector : private Timer { public: DeviceChangeDetector (const wchar_t* const name) : messageWindow (name, (WNDPROC) deviceChangeEventCallback) { SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this); } virtual ~DeviceChangeDetector() {} virtual void systemDeviceChanged() = 0; void triggerAsyncDeviceChangeCallback() { // We'll pause before sending a message, because on device removal, the OS hasn't always updated // its device lists correctly at this point. This also helps avoid repeated callbacks. startTimer (500); } private: HiddenMessageWindow messageWindow; static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam) { if (message == WM_DEVICECHANGE && (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/ || wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/ || wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/)) { ((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA)) ->triggerAsyncDeviceChangeCallback(); } return DefWindowProc (h, message, wParam, lParam); } void timerCallback() override { stopTimer(); systemDeviceChanged(); } }; #endif // JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp000066400000000000000000000171261320201440200320640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ extern HWND juce_messageWindowHandle; typedef bool (*CheckEventBlockedByModalComps) (const MSG&); CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr; //============================================================================== namespace WindowsMessageHelpers { const unsigned int specialId = WM_APP + 0x4400; const unsigned int broadcastId = WM_APP + 0x4403; const TCHAR messageWindowName[] = _T("JUCEWindow"); ScopedPointer messageWindow; void dispatchMessageFromLParam (LPARAM lParam) { MessageManager::MessageBase* const message = reinterpret_cast (lParam); JUCE_TRY { message->messageCallback(); } JUCE_CATCH_EXCEPTION message->decReferenceCount(); } //============================================================================== LRESULT CALLBACK messageWndProc (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam) noexcept { if (h == juce_messageWindowHandle) { if (message == specialId) { // (These are trapped early in our dispatch loop, but must also be checked // here in case some 3rd-party code is running the dispatch loop). dispatchMessageFromLParam (lParam); return 0; } if (message == broadcastId) { const ScopedPointer messageString ((String*) lParam); MessageManager::getInstance()->deliverBroadcastMessage (*messageString); return 0; } if (message == WM_COPYDATA) { const COPYDATASTRUCT* const data = reinterpret_cast (lParam); if (data->dwData == broadcastId) { const String messageString (CharPointer_UTF32 ((const CharPointer_UTF32::CharType*) data->lpData), data->cbData / sizeof (CharPointer_UTF32::CharType)); PostMessage (juce_messageWindowHandle, broadcastId, 0, (LPARAM) new String (messageString)); return 0; } } } return DefWindowProc (h, message, wParam, lParam); } BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam) { if (hwnd != juce_messageWindowHandle) reinterpret_cast*> (lParam)->add (hwnd); return TRUE; } } //============================================================================== bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages) { using namespace WindowsMessageHelpers; MSG m; if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, PM_NOREMOVE)) return false; if (GetMessage (&m, (HWND) 0, 0, 0) >= 0) { if (m.message == specialId && m.hwnd == juce_messageWindowHandle) { dispatchMessageFromLParam (m.lParam); } else if (m.message == WM_QUIT) { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) app->systemRequestedQuit(); } else if (isEventBlockedByModalComps == nullptr || ! isEventBlockedByModalComps (m)) { if ((m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN) && ! JuceWindowIdentifier::isJUCEWindow (m.hwnd)) { // if it's someone else's window being clicked on, and the focus is // currently on a juce window, pass the kb focus over.. HWND currentFocus = GetFocus(); if (currentFocus == 0 || JuceWindowIdentifier::isJUCEWindow (currentFocus)) SetFocus (m.hwnd); } TranslateMessage (&m); DispatchMessage (&m); } } return true; } bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { message->incReferenceCount(); return PostMessage (juce_messageWindowHandle, WindowsMessageHelpers::specialId, 0, (LPARAM) message) != 0; } void MessageManager::broadcastMessage (const String& value) { Array windows; EnumWindows (&WindowsMessageHelpers::broadcastEnumWindowProc, (LPARAM) &windows); const String localCopy (value); COPYDATASTRUCT data; data.dwData = WindowsMessageHelpers::broadcastId; data.cbData = (localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType); data.lpData = (void*) localCopy.toUTF32().getAddress(); for (int i = windows.size(); --i >= 0;) { HWND hwnd = windows.getUnchecked(i); TCHAR windowName[64] = { 0 }; // no need to read longer strings than this GetWindowText (hwnd, windowName, 63); if (String (windowName) == WindowsMessageHelpers::messageWindowName) { DWORD_PTR result; SendMessageTimeout (hwnd, WM_COPYDATA, (WPARAM) juce_messageWindowHandle, (LPARAM) &data, SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result); } } } //============================================================================== void MessageManager::doPlatformSpecificInitialisation() { OleInitialize (0); using namespace WindowsMessageHelpers; messageWindow = new HiddenMessageWindow (messageWindowName, (WNDPROC) messageWndProc); juce_messageWindowHandle = messageWindow->getHWND(); } void MessageManager::doPlatformSpecificShutdown() { WindowsMessageHelpers::messageWindow = nullptr; OleUninitialize(); } //============================================================================== struct MountedVolumeListChangeDetector::Pimpl : private DeviceChangeDetector { Pimpl (MountedVolumeListChangeDetector& d) : DeviceChangeDetector (L"MountedVolumeList"), owner (d) { File::findFileSystemRoots (lastVolumeList); } void systemDeviceChanged() override { Array newList; File::findFileSystemRoots (newList); if (lastVolumeList != newList) { lastVolumeList = newList; owner.mountedVolumeListChanged(); } } MountedVolumeListChangeDetector& owner; Array lastVolumeList; }; MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); } MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {} libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/timers/000077500000000000000000000000001320201440200254215ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp000066400000000000000000000057201320201440200312320ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct MultiTimerCallback : public Timer { MultiTimerCallback (const int tid, MultiTimer& mt) noexcept : owner (mt), timerID (tid) { } void timerCallback() override { owner.timerCallback (timerID); } MultiTimer& owner; const int timerID; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiTimerCallback) }; //============================================================================== MultiTimer::MultiTimer() noexcept {} MultiTimer::MultiTimer (const MultiTimer&) noexcept {} MultiTimer::~MultiTimer() { const SpinLock::ScopedLockType sl (timerListLock); timers.clear(); } //============================================================================== Timer* MultiTimer::getCallback (int timerID) const noexcept { for (int i = timers.size(); --i >= 0;) { MultiTimerCallback* const t = static_cast (timers.getUnchecked(i)); if (t->timerID == timerID) return t; } return nullptr; } void MultiTimer::startTimer (const int timerID, const int intervalInMilliseconds) noexcept { const SpinLock::ScopedLockType sl (timerListLock); Timer* timer = getCallback (timerID); if (timer == nullptr) timers.add (timer = new MultiTimerCallback (timerID, *this)); timer->startTimer (intervalInMilliseconds); } void MultiTimer::stopTimer (const int timerID) noexcept { const SpinLock::ScopedLockType sl (timerListLock); if (Timer* const t = getCallback (timerID)) t->stopTimer(); } bool MultiTimer::isTimerRunning (const int timerID) const noexcept { const SpinLock::ScopedLockType sl (timerListLock); if (Timer* const t = getCallback (timerID)) return t->isTimerRunning(); return false; } int MultiTimer::getTimerInterval (const int timerID) const noexcept { const SpinLock::ScopedLockType sl (timerListLock); if (Timer* const t = getCallback (timerID)) return t->getTimerInterval(); return 0; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h000066400000000000000000000116301320201440200306740ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MULTITIMER_H_INCLUDED #define JUCE_MULTITIMER_H_INCLUDED //============================================================================== /** A type of timer class that can run multiple timers with different frequencies, all of which share a single callback. This class is very similar to the Timer class, but allows you run multiple separate timers, where each one has a unique ID number. The methods in this class are exactly equivalent to those in Timer, but with the addition of this ID number. To use it, you need to create a subclass of MultiTimer, implementing the timerCallback() method. Then you can start timers with startTimer(), and each time the callback is triggered, it passes in the ID of the timer that caused it. @see Timer */ class JUCE_API MultiTimer { protected: //============================================================================== /** Creates a MultiTimer. When created, no timers are running, so use startTimer() to start things off. */ MultiTimer() noexcept; /** Creates a copy of another timer. Note that this timer will not contain any running timers, even if the one you're copying from was running. */ MultiTimer (const MultiTimer&) noexcept; public: //============================================================================== /** Destructor. */ virtual ~MultiTimer(); //============================================================================== /** The user-defined callback routine that actually gets called by each of the timers that are running. It's perfectly ok to call startTimer() or stopTimer() from within this callback to change the subsequent intervals. */ virtual void timerCallback (int timerID) = 0; //============================================================================== /** Starts a timer and sets the length of interval required. If the timer is already started, this will reset it, so the time between calling this method and the next timer callback will not be less than the interval length passed in. @param timerID a unique Id number that identifies the timer to start. This is the id that will be passed back to the timerCallback() method when this timer is triggered @param intervalInMilliseconds the interval to use (any values less than 1 will be rounded up to 1) */ void startTimer (int timerID, int intervalInMilliseconds) noexcept; /** Stops a timer. If a timer has been started with the given ID number, it will be cancelled. No more callbacks will be made for the specified timer after this method returns. If this is called from a different thread, any callbacks that may be currently executing may be allowed to finish before the method returns. */ void stopTimer (int timerID) noexcept; //============================================================================== /** Checks whether a timer has been started for a specified ID. @returns true if a timer with the given ID is running. */ bool isTimerRunning (int timerID) const noexcept; /** Returns the interval for a specified timer ID. @returns the timer's interval in milliseconds if it's running, or 0 if no timer was running for the ID number specified. */ int getTimerInterval (int timerID) const noexcept; //============================================================================== private: SpinLock timerListLock; OwnedArray timers; Timer* getCallback (int) const noexcept; MultiTimer& operator= (const MultiTimer&); }; #endif // JUCE_MULTITIMER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp000066400000000000000000000253361320201440200302240ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class Timer::TimerThread : private Thread, private DeletedAtShutdown, private AsyncUpdater { public: typedef CriticalSection LockType; // (mysteriously, using a SpinLock here causes problems on some XP machines..) TimerThread() : Thread ("Juce Timer"), firstTimer (nullptr), callbackNeeded (0) { triggerAsyncUpdate(); } ~TimerThread() noexcept { stopThread (4000); jassert (instance == this || instance == nullptr); if (instance == this) instance = nullptr; } void run() override { uint32 lastTime = Time::getMillisecondCounter(); MessageManager::MessageBase::Ptr messageToSend (new CallTimersMessage()); while (! threadShouldExit()) { const uint32 now = Time::getMillisecondCounter(); if (now == lastTime) { wait (1); continue; } const int elapsed = (int) (now >= lastTime ? (now - lastTime) : (std::numeric_limits::max() - (lastTime - now))); lastTime = now; const int timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed); if (timeUntilFirstTimer <= 0) { /* If we managed to set the atomic boolean to true then send a message, this is needed as a memory barrier so the message won't be sent before callbackNeeded is set to true, but if it fails it means the message-thread changed the value from under us so at least some processing is happenening and we can just loop around and try again */ if (callbackNeeded.compareAndSetBool (1, 0)) { messageToSend->post(); /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS when the app has a modal loop), so this is how long to wait before assuming the message has been lost and trying again. */ const uint32 messageDeliveryTimeout = now + 300; while (callbackNeeded.get() != 0) { wait (4); if (threadShouldExit()) return; if (Time::getMillisecondCounter() > messageDeliveryTimeout) { messageToSend->post(); break; } } } } else { // don't wait for too long because running this loop also helps keep the // Time::getApproximateMillisecondTimer value stay up-to-date wait (jlimit (1, 50, timeUntilFirstTimer)); } } } void callTimers() { const LockType::ScopedLockType sl (lock); while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0) { Timer* const t = firstTimer; t->timerCountdownMs = t->timerPeriodMs; removeTimer (t); addTimer (t); const LockType::ScopedUnlockType ul (lock); JUCE_TRY { t->timerCallback(); } JUCE_CATCH_EXCEPTION } /* This is needed as a memory barrier to make sure all processing of current timers is done before the boolean is set. This set should never fail since if it was false in the first place, we wouldn't get a message (so it can't be changed from false to true from under us), and if we get a message then the value is true and the other thread can only set it to true again and we will get another callback to set it to false. */ callbackNeeded.set (0); } void callTimersSynchronously() { if (! isThreadRunning()) { // (This is relied on by some plugins in cases where the MM has // had to restart and the async callback never started) cancelPendingUpdate(); triggerAsyncUpdate(); } callTimers(); } static inline void add (Timer* const tim) noexcept { if (instance == nullptr) instance = new TimerThread(); instance->addTimer (tim); } static inline void remove (Timer* const tim) noexcept { if (instance != nullptr) instance->removeTimer (tim); } static inline void resetCounter (Timer* const tim, const int newCounter) noexcept { if (instance != nullptr) { tim->timerCountdownMs = newCounter; tim->timerPeriodMs = newCounter; if ((tim->nextTimer != nullptr && tim->nextTimer->timerCountdownMs < tim->timerCountdownMs) || (tim->previousTimer != nullptr && tim->previousTimer->timerCountdownMs > tim->timerCountdownMs)) { instance->removeTimer (tim); instance->addTimer (tim); } } } static TimerThread* instance; static LockType lock; private: Timer* volatile firstTimer; Atomic callbackNeeded; struct CallTimersMessage : public MessageManager::MessageBase { CallTimersMessage() {} void messageCallback() override { if (instance != nullptr) instance->callTimers(); } }; //============================================================================== void addTimer (Timer* const t) noexcept { #if JUCE_DEBUG // trying to add a timer that's already here - shouldn't get to this point, // so if you get this assertion, let me know! jassert (! timerExists (t)); #endif Timer* i = firstTimer; if (i == nullptr || i->timerCountdownMs > t->timerCountdownMs) { t->nextTimer = firstTimer; firstTimer = t; } else { while (i->nextTimer != nullptr && i->nextTimer->timerCountdownMs <= t->timerCountdownMs) i = i->nextTimer; jassert (i != nullptr); t->nextTimer = i->nextTimer; t->previousTimer = i; i->nextTimer = t; } if (t->nextTimer != nullptr) t->nextTimer->previousTimer = t; jassert ((t->nextTimer == nullptr || t->nextTimer->timerCountdownMs >= t->timerCountdownMs) && (t->previousTimer == nullptr || t->previousTimer->timerCountdownMs <= t->timerCountdownMs)); notify(); } void removeTimer (Timer* const t) noexcept { #if JUCE_DEBUG // trying to remove a timer that's not here - shouldn't get to this point, // so if you get this assertion, let me know! jassert (timerExists (t)); #endif if (t->previousTimer != nullptr) { jassert (firstTimer != t); t->previousTimer->nextTimer = t->nextTimer; } else { jassert (firstTimer == t); firstTimer = t->nextTimer; } if (t->nextTimer != nullptr) t->nextTimer->previousTimer = t->previousTimer; t->nextTimer = nullptr; t->previousTimer = nullptr; } int getTimeUntilFirstTimer (const int numMillisecsElapsed) const { const LockType::ScopedLockType sl (lock); for (Timer* t = firstTimer; t != nullptr; t = t->nextTimer) t->timerCountdownMs -= numMillisecsElapsed; return firstTimer != nullptr ? firstTimer->timerCountdownMs : 1000; } void handleAsyncUpdate() override { startThread (7); } #if JUCE_DEBUG bool timerExists (Timer* const t) const noexcept { for (Timer* tt = firstTimer; tt != nullptr; tt = tt->nextTimer) if (tt == t) return true; return false; } #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread) }; Timer::TimerThread* Timer::TimerThread::instance = nullptr; Timer::TimerThread::LockType Timer::TimerThread::lock; //============================================================================== Timer::Timer() noexcept : timerCountdownMs (0), timerPeriodMs (0), previousTimer (nullptr), nextTimer (nullptr) { } Timer::Timer (const Timer&) noexcept : timerCountdownMs (0), timerPeriodMs (0), previousTimer (nullptr), nextTimer (nullptr) { } Timer::~Timer() { stopTimer(); } void Timer::startTimer (const int interval) noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); if (timerPeriodMs == 0) { timerCountdownMs = interval; timerPeriodMs = jmax (1, interval); TimerThread::add (this); } else { TimerThread::resetCounter (this, interval); } } void Timer::startTimerHz (int timerFrequencyHz) noexcept { if (timerFrequencyHz > 0) startTimer (1000 / timerFrequencyHz); else stopTimer(); } void Timer::stopTimer() noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); if (timerPeriodMs > 0) { TimerThread::remove (this); timerPeriodMs = 0; } } void JUCE_CALLTYPE Timer::callPendingTimersSynchronously() { if (TimerThread::instance != nullptr) TimerThread::instance->callTimersSynchronously(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h000066400000000000000000000124501320201440200276620ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_TIMER_H_INCLUDED #define JUCE_TIMER_H_INCLUDED //============================================================================== /** Makes repeated callbacks to a virtual method at a specified time interval. A Timer's timerCallback() method will be repeatedly called at a given interval. When you create a Timer object, it will do nothing until the startTimer() method is called, which will cause the message thread to start making callbacks at the specified interval, until stopTimer() is called or the object is deleted. The time interval isn't guaranteed to be precise to any more than maybe 10-20ms, and the intervals may end up being much longer than requested if the system is busy. Because the callbacks are made by the main message thread, anything that blocks the message queue for a period of time will also prevent any timers from running until it can carry on. If you need to have a single callback that is shared by multiple timers with different frequencies, then the MultiTimer class allows you to do that - its structure is very similar to the Timer class, but contains multiple timers internally, each one identified by an ID number. @see HighResolutionTimer, MultiTimer */ class JUCE_API Timer { protected: //============================================================================== /** Creates a Timer. When created, the timer is stopped, so use startTimer() to get it going. */ Timer() noexcept; /** Creates a copy of another timer. Note that this timer won't be started, even if the one you're copying is running. */ Timer (const Timer&) noexcept; public: //============================================================================== /** Destructor. */ virtual ~Timer(); //============================================================================== /** The user-defined callback routine that actually gets called periodically. It's perfectly ok to call startTimer() or stopTimer() from within this callback to change the subsequent intervals. */ virtual void timerCallback() = 0; //============================================================================== /** Starts the timer and sets the length of interval required. If the timer is already started, this will reset it, so the time between calling this method and the next timer callback will not be less than the interval length passed in. @param intervalInMilliseconds the interval to use (any value less than 1 will be rounded up to 1) */ void startTimer (int intervalInMilliseconds) noexcept; /** Starts the timer with an interval specified in Hertz. This is effectively the same as calling startTimer (1000 / timerFrequencyHz). */ void startTimerHz (int timerFrequencyHz) noexcept; /** Stops the timer. No more callbacks will be made after this method returns. If this is called from a different thread, any callbacks that may be currently executing may be allowed to finish before the method returns. */ void stopTimer() noexcept; //============================================================================== /** Returns true if the timer is currently running. */ bool isTimerRunning() const noexcept { return timerPeriodMs > 0; } /** Returns the timer's interval. @returns the timer's interval in milliseconds if it's running, or 0 if it's not. */ int getTimerInterval() const noexcept { return timerPeriodMs; } //============================================================================== /** For internal use only: invokes any timers that need callbacks. Don't call this unless you really know what you're doing! */ static void JUCE_CALLTYPE callPendingTimersSynchronously(); private: class TimerThread; friend class TimerThread; int timerCountdownMs, timerPeriodMs; // NB: these member variable names are a little verbose Timer* previousTimer, *nextTimer; // to reduce risk of name-clashes with user subclasses Timer& operator= (const Timer&) JUCE_DELETED_FUNCTION; }; #endif // JUCE_TIMER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/000077500000000000000000000000001320201440200244125ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/000077500000000000000000000000001320201440200257155ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp000066400000000000000000000362631320201440200307040ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace ColourHelpers { static uint8 floatToUInt8 (const float n) noexcept { return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : static_cast (n * 255.996f)); } //============================================================================== struct HSB { HSB (Colour col) noexcept { const int r = col.getRed(); const int g = col.getGreen(); const int b = col.getBlue(); const int hi = jmax (r, g, b); const int lo = jmin (r, g, b); if (hi != 0) { saturation = (hi - lo) / (float) hi; if (saturation > 0) { const float invDiff = 1.0f / (hi - lo); const float red = (hi - r) * invDiff; const float green = (hi - g) * invDiff; const float blue = (hi - b) * invDiff; if (r == hi) hue = blue - green; else if (g == hi) hue = 2.0f + red - blue; else hue = 4.0f + green - red; hue *= 1.0f / 6.0f; if (hue < 0) ++hue; } else { hue = 0; } } else { saturation = hue = 0; } brightness = hi / 255.0f; } Colour toColour (Colour original) const noexcept { return Colour (hue, saturation, brightness, original.getAlpha()); } static PixelARGB toRGB (float h, float s, float v, const uint8 alpha) noexcept { v = jlimit (0.0f, 255.0f, v * 255.0f); const uint8 intV = (uint8) roundToInt (v); if (s <= 0) return PixelARGB (alpha, intV, intV, intV); s = jmin (1.0f, s); h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors const float f = h - std::floor (h); const uint8 x = (uint8) roundToInt (v * (1.0f - s)); if (h < 1.0f) return PixelARGB (alpha, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x); if (h < 2.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - s * f)), intV, x); if (h < 3.0f) return PixelARGB (alpha, x, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f))))); if (h < 4.0f) return PixelARGB (alpha, x, (uint8) roundToInt (v * (1.0f - s * f)), intV); if (h < 5.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x, intV); return PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f))); } float hue, saturation, brightness; }; //============================================================================== struct YIQ { YIQ (Colour c) noexcept { const float r = c.getFloatRed(); const float g = c.getFloatGreen(); const float b = c.getFloatBlue(); y = 0.2999f * r + 0.5870f * g + 0.1140f * b; i = 0.5957f * r - 0.2744f * g - 0.3212f * b; q = 0.2114f * r - 0.5225f * g - 0.3113f * b; alpha = c.getFloatAlpha(); } Colour toColour() const noexcept { return Colour::fromFloatRGBA (y + 0.9563f * i + 0.6210f * q, y - 0.2721f * i - 0.6474f * q, y - 1.1070f * i + 1.7046f * q, alpha); } float y, i, q, alpha; }; } //============================================================================== Colour::Colour() noexcept : argb (0, 0, 0, 0) { } Colour::Colour (const Colour& other) noexcept : argb (other.argb) { } Colour& Colour::operator= (const Colour& other) noexcept { argb = other.argb; return *this; } bool Colour::operator== (const Colour& other) const noexcept { return argb.getNativeARGB() == other.argb.getNativeARGB(); } bool Colour::operator!= (const Colour& other) const noexcept { return argb.getNativeARGB() != other.argb.getNativeARGB(); } //============================================================================== Colour::Colour (const uint32 col) noexcept : argb ((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff) { } Colour::Colour (const uint8 red, const uint8 green, const uint8 blue) noexcept { argb.setARGB (0xff, red, green, blue); } Colour Colour::fromRGB (const uint8 red, const uint8 green, const uint8 blue) noexcept { return Colour (red, green, blue); } Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept { argb.setARGB (alpha, red, green, blue); } Colour Colour::fromRGBA (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept { return Colour (red, green, blue, alpha); } Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const float alpha) noexcept { argb.setARGB (ColourHelpers::floatToUInt8 (alpha), red, green, blue); } Colour Colour::fromFloatRGBA (const float red, const float green, const float blue, const float alpha) noexcept { return Colour (ColourHelpers::floatToUInt8 (red), ColourHelpers::floatToUInt8 (green), ColourHelpers::floatToUInt8 (blue), alpha); } Colour::Colour (const float hue, const float saturation, const float brightness, const float alpha) noexcept : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha))) { } Colour Colour::fromHSV (const float hue, const float saturation, const float brightness, const float alpha) noexcept { return Colour (hue, saturation, brightness, alpha); } Colour::Colour (const float hue, const float saturation, const float brightness, const uint8 alpha) noexcept : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, alpha)) { } Colour::Colour (PixelARGB argb_) noexcept : argb (argb_) { } Colour::Colour (PixelRGB rgb) noexcept : argb (Colour (rgb.getInARGBMaskOrder()).argb) { } Colour::Colour (PixelAlpha alpha) noexcept : argb (Colour (alpha.getInARGBMaskOrder()).argb) { } Colour::~Colour() noexcept { } //============================================================================== const PixelARGB Colour::getPixelARGB() const noexcept { PixelARGB p (argb); p.premultiply(); return p; } uint32 Colour::getARGB() const noexcept { return argb.getInARGBMaskOrder(); } //============================================================================== bool Colour::isTransparent() const noexcept { return getAlpha() == 0; } bool Colour::isOpaque() const noexcept { return getAlpha() == 0xff; } Colour Colour::withAlpha (const uint8 newAlpha) const noexcept { PixelARGB newCol (argb); newCol.setAlpha (newAlpha); return Colour (newCol); } Colour Colour::withAlpha (const float newAlpha) const noexcept { jassert (newAlpha >= 0 && newAlpha <= 1.0f); PixelARGB newCol (argb); newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha)); return Colour (newCol); } Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept { jassert (alphaMultiplier >= 0); PixelARGB newCol (argb); newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha()))); return Colour (newCol); } //============================================================================== Colour Colour::overlaidWith (Colour src) const noexcept { const int destAlpha = getAlpha(); if (destAlpha <= 0) return src; const int invA = 0xff - (int) src.getAlpha(); const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8); if (resA <= 0) return *this; const int da = (invA * destAlpha) / resA; return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)), (uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)), (uint8) (src.getBlue() + ((((int) getBlue() - src.getBlue()) * da) >> 8)), (uint8) resA); } Colour Colour::interpolatedWith (Colour other, float proportionOfOther) const noexcept { if (proportionOfOther <= 0) return *this; if (proportionOfOther >= 1.0f) return other; PixelARGB c1 (getPixelARGB()); const PixelARGB c2 (other.getPixelARGB()); c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f)); c1.unpremultiply(); return Colour (c1); } //============================================================================== float Colour::getFloatRed() const noexcept { return getRed() / 255.0f; } float Colour::getFloatGreen() const noexcept { return getGreen() / 255.0f; } float Colour::getFloatBlue() const noexcept { return getBlue() / 255.0f; } float Colour::getFloatAlpha() const noexcept { return getAlpha() / 255.0f; } //============================================================================== void Colour::getHSB (float& h, float& s, float& v) const noexcept { const ColourHelpers::HSB hsb (*this); h = hsb.hue; s = hsb.saturation; v = hsb.brightness; } float Colour::getHue() const noexcept { return ColourHelpers::HSB (*this).hue; } float Colour::getSaturation() const noexcept { return ColourHelpers::HSB (*this).saturation; } float Colour::getBrightness() const noexcept { return ColourHelpers::HSB (*this).brightness; } Colour Colour::withHue (float h) const noexcept { ColourHelpers::HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); } Colour Colour::withSaturation (float s) const noexcept { ColourHelpers::HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); } Colour Colour::withBrightness (float v) const noexcept { ColourHelpers::HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); } float Colour::getPerceivedBrightness() const noexcept { return std::sqrt (0.241f * square (getFloatRed()) + 0.691f * square (getFloatGreen()) + 0.068f * square (getFloatBlue())); } //============================================================================== Colour Colour::withRotatedHue (const float amountToRotate) const noexcept { ColourHelpers::HSB hsb (*this); hsb.hue += amountToRotate; return hsb.toColour (*this); } Colour Colour::withMultipliedSaturation (const float amount) const noexcept { ColourHelpers::HSB hsb (*this); hsb.saturation = jmin (1.0f, hsb.saturation * amount); return hsb.toColour (*this); } Colour Colour::withMultipliedBrightness (const float amount) const noexcept { ColourHelpers::HSB hsb (*this); hsb.brightness = jmin (1.0f, hsb.brightness * amount); return hsb.toColour (*this); } //============================================================================== Colour Colour::brighter (float amount) const noexcept { amount = 1.0f / (1.0f + amount); return Colour ((uint8) (255 - (amount * (255 - getRed()))), (uint8) (255 - (amount * (255 - getGreen()))), (uint8) (255 - (amount * (255 - getBlue()))), getAlpha()); } Colour Colour::darker (float amount) const noexcept { amount = 1.0f / (1.0f + amount); return Colour ((uint8) (amount * getRed()), (uint8) (amount * getGreen()), (uint8) (amount * getBlue()), getAlpha()); } //============================================================================== Colour Colour::greyLevel (const float brightness) noexcept { const uint8 level = ColourHelpers::floatToUInt8 (brightness); return Colour (level, level, level); } //============================================================================== Colour Colour::contrasting (const float amount) const noexcept { return overlaidWith ((getPerceivedBrightness() >= 0.5f ? Colours::black : Colours::white).withAlpha (amount)); } Colour Colour::contrasting (Colour target, float minContrast) const noexcept { const ColourHelpers::YIQ bg (*this); ColourHelpers::YIQ fg (target); if (std::abs (bg.y - fg.y) >= minContrast) return target; const float y1 = jmax (0.0f, bg.y - minContrast); const float y2 = jmin (1.0f, bg.y + minContrast); fg.y = (std::abs (y1 - bg.y) > std::abs (y2 - bg.y)) ? y1 : y2; return fg.toColour(); } Colour Colour::contrasting (Colour colour1, Colour colour2) noexcept { const float b1 = colour1.getPerceivedBrightness(); const float b2 = colour2.getPerceivedBrightness(); float best = 0.0f; float bestDist = 0.0f; for (float i = 0.0f; i < 1.0f; i += 0.02f) { const float d1 = std::abs (i - b1); const float d2 = std::abs (i - b2); const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2); if (dist > bestDist) { best = i; bestDist = dist; } } return colour1.overlaidWith (colour2.withMultipliedAlpha (0.5f)) .withBrightness (best); } //============================================================================== String Colour::toString() const { return String::toHexString ((int) argb.getInARGBMaskOrder()); } Colour Colour::fromString (StringRef encodedColourString) { return Colour ((uint32) CharacterFunctions::HexParser::parse (encodedColourString.text)); } String Colour::toDisplayString (const bool includeAlphaValue) const { return String::toHexString ((int) (argb.getInARGBMaskOrder() & (includeAlphaValue ? 0xffffffff : 0xffffff))) .paddedLeft ('0', includeAlphaValue ? 8 : 6) .toUpperCase(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h000066400000000000000000000345011320201440200303420ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_COLOUR_H_INCLUDED #define JUCE_COLOUR_H_INCLUDED //============================================================================== /** Represents a colour, also including a transparency value. The colour is stored internally as unsigned 8-bit red, green, blue and alpha values. */ class JUCE_API Colour { public: //============================================================================== /** Creates a transparent black colour. */ Colour() noexcept; /** Creates a copy of another Colour object. */ Colour (const Colour& other) noexcept; /** Creates a colour from a 32-bit ARGB value. The format of this number is: ((alpha << 24) | (red << 16) | (green << 8) | blue). All components in the range 0x00 to 0xff. An alpha of 0x00 is completely transparent, alpha of 0xff is opaque. @see getPixelARGB */ explicit Colour (uint32 argb) noexcept; /** Creates an opaque colour using 8-bit red, green and blue values */ Colour (uint8 red, uint8 green, uint8 blue) noexcept; /** Creates an opaque colour using 8-bit red, green and blue values */ static Colour fromRGB (uint8 red, uint8 green, uint8 blue) noexcept; /** Creates a colour using 8-bit red, green, blue and alpha values. */ Colour (uint8 red, uint8 green, uint8 blue, uint8 alpha) noexcept; /** Creates a colour using 8-bit red, green, blue and alpha values. */ static Colour fromRGBA (uint8 red, uint8 green, uint8 blue, uint8 alpha) noexcept; /** Creates a colour from 8-bit red, green, and blue values, and a floating-point alpha. Alpha of 0.0 is transparent, alpha of 1.0f is opaque. Values outside the valid range will be clipped. */ Colour (uint8 red, uint8 green, uint8 blue, float alpha) noexcept; /** Creates a colour using floating point red, green, blue and alpha values. Numbers outside the range 0..1 will be clipped. */ static Colour fromFloatRGBA (float red, float green, float blue, float alpha) noexcept; /** Creates a colour using floating point hue, saturation and brightness values, and an 8-bit alpha. The floating point values must be between 0.0 and 1.0. An alpha of 0x00 is completely transparent, alpha of 0xff is opaque. Values outside the valid range will be clipped. */ Colour (float hue, float saturation, float brightness, uint8 alpha) noexcept; /** Creates a colour using floating point hue, saturation, brightness and alpha values. All values must be between 0.0 and 1.0. Numbers outside the valid range will be clipped. */ Colour (float hue, float saturation, float brightness, float alpha) noexcept; /** Creates a colour using a PixelARGB object. This function assumes that the argb pixel is not premultiplied. */ Colour (PixelARGB argb) noexcept; /** Creates a colour using a PixelRGB object. */ Colour (PixelRGB rgb) noexcept; /** Creates a colour using a PixelAlpha object. */ Colour (PixelAlpha alpha) noexcept; /** Creates a colour using floating point hue, saturation and brightness values, and an 8-bit alpha. The floating point values must be between 0.0 and 1.0. An alpha of 0x00 is completely transparent, alpha of 0xff is opaque. Values outside the valid range will be clipped. */ static Colour fromHSV (float hue, float saturation, float brightness, float alpha) noexcept; /** Destructor. */ ~Colour() noexcept; /** Copies another Colour object. */ Colour& operator= (const Colour& other) noexcept; /** Compares two colours. */ bool operator== (const Colour& other) const noexcept; /** Compares two colours. */ bool operator!= (const Colour& other) const noexcept; //============================================================================== /** Returns the red component of this colour. @returns a value between 0x00 and 0xff. */ uint8 getRed() const noexcept { return argb.getRed(); } /** Returns the green component of this colour. @returns a value between 0x00 and 0xff. */ uint8 getGreen() const noexcept { return argb.getGreen(); } /** Returns the blue component of this colour. @returns a value between 0x00 and 0xff. */ uint8 getBlue() const noexcept { return argb.getBlue(); } /** Returns the red component of this colour as a floating point value. @returns a value between 0.0 and 1.0 */ float getFloatRed() const noexcept; /** Returns the green component of this colour as a floating point value. @returns a value between 0.0 and 1.0 */ float getFloatGreen() const noexcept; /** Returns the blue component of this colour as a floating point value. @returns a value between 0.0 and 1.0 */ float getFloatBlue() const noexcept; /** Returns a premultiplied ARGB pixel object that represents this colour. */ const PixelARGB getPixelARGB() const noexcept; /** Returns a 32-bit integer that represents this colour. The format of this number is: ((alpha << 24) | (red << 16) | (green << 16) | blue). */ uint32 getARGB() const noexcept; //============================================================================== /** Returns the colour's alpha (opacity). Alpha of 0x00 is completely transparent, 0xff is completely opaque. */ uint8 getAlpha() const noexcept { return argb.getAlpha(); } /** Returns the colour's alpha (opacity) as a floating point value. Alpha of 0.0 is completely transparent, 1.0 is completely opaque. */ float getFloatAlpha() const noexcept; /** Returns true if this colour is completely opaque. Equivalent to (getAlpha() == 0xff). */ bool isOpaque() const noexcept; /** Returns true if this colour is completely transparent. Equivalent to (getAlpha() == 0x00). */ bool isTransparent() const noexcept; /** Returns a colour that's the same colour as this one, but with a new alpha value. */ Colour withAlpha (uint8 newAlpha) const noexcept; /** Returns a colour that's the same colour as this one, but with a new alpha value. */ Colour withAlpha (float newAlpha) const noexcept; /** Returns a colour that's the same colour as this one, but with a modified alpha value. The new colour's alpha will be this object's alpha multiplied by the value passed-in. */ Colour withMultipliedAlpha (float alphaMultiplier) const noexcept; //============================================================================== /** Returns a colour that is the result of alpha-compositing a new colour over this one. If the foreground colour is semi-transparent, it is blended onto this colour accordingly. */ Colour overlaidWith (Colour foregroundColour) const noexcept; /** Returns a colour that lies somewhere between this one and another. If amountOfOther is zero, the result is 100% this colour, if amountOfOther is 1.0, the result is 100% of the other colour. */ Colour interpolatedWith (Colour other, float proportionOfOther) const noexcept; //============================================================================== /** Returns the colour's hue component. The value returned is in the range 0.0 to 1.0 */ float getHue() const noexcept; /** Returns the colour's saturation component. The value returned is in the range 0.0 to 1.0 */ float getSaturation() const noexcept; /** Returns the colour's brightness component. The value returned is in the range 0.0 to 1.0 */ float getBrightness() const noexcept; /** Returns a skewed brightness value, adjusted to better reflect the way the human eye responds to different colour channels. This makes it better than getBrightness() for comparing differences in brightness. */ float getPerceivedBrightness() const noexcept; /** Returns the colour's hue, saturation and brightness components all at once. The values returned are in the range 0.0 to 1.0 */ void getHSB (float& hue, float& saturation, float& brightness) const noexcept; //============================================================================== /** Returns a copy of this colour with a different hue. */ Colour withHue (float newHue) const noexcept; /** Returns a copy of this colour with a different saturation. */ Colour withSaturation (float newSaturation) const noexcept; /** Returns a copy of this colour with a different brightness. @see brighter, darker, withMultipliedBrightness */ Colour withBrightness (float newBrightness) const noexcept; /** Returns a copy of this colour with it hue rotated. The new colour's hue is ((this->getHue() + amountToRotate) % 1.0) @see brighter, darker, withMultipliedBrightness */ Colour withRotatedHue (float amountToRotate) const noexcept; /** Returns a copy of this colour with its saturation multiplied by the given value. The new colour's saturation is (this->getSaturation() * multiplier) (the result is clipped to legal limits). */ Colour withMultipliedSaturation (float multiplier) const noexcept; /** Returns a copy of this colour with its brightness multiplied by the given value. The new colour's saturation is (this->getBrightness() * multiplier) (the result is clipped to legal limits). */ Colour withMultipliedBrightness (float amount) const noexcept; //============================================================================== /** Returns a brighter version of this colour. @param amountBrighter how much brighter to make it - a value from 0 to 1.0 where 0 is unchanged, and higher values make it brighter @see withMultipliedBrightness */ Colour brighter (float amountBrighter = 0.4f) const noexcept; /** Returns a darker version of this colour. @param amountDarker how much darker to make it - a value from 0 to 1.0 where 0 is unchanged, and higher values make it darker @see withMultipliedBrightness */ Colour darker (float amountDarker = 0.4f) const noexcept; //============================================================================== /** Returns a colour that will be clearly visible against this colour. The amount parameter indicates how contrasting the new colour should be, so e.g. Colours::black.contrasting (0.1f) will return a colour that's just a little bit lighter; Colours::black.contrasting (1.0f) will return white; Colours::white.contrasting (1.0f) will return black, etc. */ Colour contrasting (float amount = 1.0f) const noexcept; /** Returns a colour that is as close as possible to a target colour whilst still being in contrast to this one. The colour that is returned will be the targetColour, but with its luminosity nudged up or down so that it differs from the luminosity of this colour by at least the amount specified by minLuminosityDiff. */ Colour contrasting (Colour targetColour, float minLuminosityDiff) const noexcept; /** Returns a colour that contrasts against two colours. Looks for a colour that contrasts with both of the colours passed-in. Handy for things like choosing a highlight colour in text editors, etc. */ static Colour contrasting (Colour colour1, Colour colour2) noexcept; //============================================================================== /** Returns an opaque shade of grey. @param brightness the level of grey to return - 0 is black, 1.0 is white */ static Colour greyLevel (float brightness) noexcept; //============================================================================== /** Returns a stringified version of this colour. The string can be turned back into a colour using the fromString() method. */ String toString() const; /** Reads the colour from a string that was created with toString(). */ static Colour fromString (StringRef encodedColourString); /** Returns the colour as a hex string in the form RRGGBB or AARRGGBB. */ String toDisplayString (bool includeAlphaValue) const; private: //============================================================================== PixelARGB argb; }; #endif // JUCE_COLOUR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp000066400000000000000000000156371320201440200323640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ColourGradient::ColourGradient() noexcept { #if JUCE_DEBUG point1.setX (987654.0f); #define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED jassert (point1.x != 987654.0f); #else #define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED #endif } ColourGradient::ColourGradient (Colour colour1, const float x1, const float y1, Colour colour2, const float x2, const float y2, const bool radial) : point1 (x1, y1), point2 (x2, y2), isRadial (radial) { colours.add (ColourPoint (0.0, colour1)); colours.add (ColourPoint (1.0, colour2)); } ColourGradient::~ColourGradient() { } bool ColourGradient::operator== (const ColourGradient& other) const noexcept { return point1 == other.point1 && point2 == other.point2 && isRadial == other.isRadial && colours == other.colours; } bool ColourGradient::operator!= (const ColourGradient& other) const noexcept { return ! operator== (other); } //============================================================================== void ColourGradient::clearColours() { colours.clear(); } int ColourGradient::addColour (const double proportionAlongGradient, Colour colour) { // must be within the two end-points jassert (proportionAlongGradient >= 0 && proportionAlongGradient <= 1.0); const double pos = jlimit (0.0, 1.0, proportionAlongGradient); int i; for (i = 0; i < colours.size(); ++i) if (colours.getReference(i).position > pos) break; colours.insert (i, ColourPoint (pos, colour)); return i; } void ColourGradient::removeColour (int index) { jassert (index > 0 && index < colours.size() - 1); colours.remove (index); } void ColourGradient::multiplyOpacity (const float multiplier) noexcept { for (int i = 0; i < colours.size(); ++i) { Colour& c = colours.getReference(i).colour; c = c.withMultipliedAlpha (multiplier); } } //============================================================================== int ColourGradient::getNumColours() const noexcept { return colours.size(); } double ColourGradient::getColourPosition (const int index) const noexcept { if (isPositiveAndBelow (index, colours.size())) return colours.getReference (index).position; return 0; } Colour ColourGradient::getColour (const int index) const noexcept { if (isPositiveAndBelow (index, colours.size())) return colours.getReference (index).colour; return Colour(); } void ColourGradient::setColour (int index, Colour newColour) noexcept { if (isPositiveAndBelow (index, colours.size())) colours.getReference (index).colour = newColour; } Colour ColourGradient::getColourAtPosition (const double position) const noexcept { jassert (colours.getReference(0).position == 0); // the first colour specified has to go at position 0 if (position <= 0 || colours.size() <= 1) return colours.getReference(0).colour; int i = colours.size() - 1; while (position < colours.getReference(i).position) --i; const ColourPoint& p1 = colours.getReference (i); if (i >= colours.size() - 1) return p1.colour; const ColourPoint& p2 = colours.getReference (i + 1); return p1.colour.interpolatedWith (p2.colour, (float) ((position - p1.position) / (p2.position - p1.position))); } //============================================================================== void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int numEntries) const noexcept { JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its coordinates? jassert (colours.size() >= 2); jassert (numEntries > 0); jassert (colours.getReference(0).position == 0); // The first colour specified has to go at position 0 PixelARGB pix1 (colours.getReference (0).colour.getPixelARGB()); int index = 0; for (int j = 1; j < colours.size(); ++j) { const ColourPoint& p = colours.getReference (j); const int numToDo = roundToInt (p.position * (numEntries - 1)) - index; const PixelARGB pix2 (p.colour.getPixelARGB()); for (int i = 0; i < numToDo; ++i) { jassert (index >= 0 && index < numEntries); lookupTable[index] = pix1; lookupTable[index].tween (pix2, (uint32) ((i << 8) / numToDo)); ++index; } pix1 = pix2; } while (index < numEntries) lookupTable [index++] = pix1; } int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock & lookupTable) const { JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its coordinates? jassert (colours.size() >= 2); const int numEntries = jlimit (1, jmax (1, (colours.size() - 1) << 8), 3 * (int) point1.transformedBy (transform) .getDistanceFrom (point2.transformedBy (transform))); lookupTable.malloc ((size_t) numEntries); createLookupTable (lookupTable, numEntries); return numEntries; } bool ColourGradient::isOpaque() const noexcept { for (int i = 0; i < colours.size(); ++i) if (! colours.getReference(i).colour.isOpaque()) return false; return true; } bool ColourGradient::isInvisible() const noexcept { for (int i = 0; i < colours.size(); ++i) if (! colours.getReference(i).colour.isTransparent()) return false; return true; } bool ColourGradient::ColourPoint::operator== (const ColourPoint& other) const noexcept { return position == other.position && colour == other.colour; } bool ColourGradient::ColourPoint::operator!= (const ColourPoint& other) const noexcept { return position != other.position || colour != other.colour; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h000066400000000000000000000157131320201440200320240ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_COLOURGRADIENT_H_INCLUDED #define JUCE_COLOURGRADIENT_H_INCLUDED //============================================================================== /** Describes the layout and colours that should be used to paint a colour gradient. @see Graphics::setGradientFill */ class JUCE_API ColourGradient { public: //============================================================================== /** Creates a gradient object. (x1, y1) is the location to draw with colour1. Likewise (x2, y2) is where colour2 should be. In between them there's a gradient. If isRadial is true, the colours form a circular gradient with (x1, y1) at its centre. The alpha transparencies of the colours are used, so note that if you blend from transparent to a solid colour, the RGB of the transparent colour will become visible in parts of the gradient. e.g. blending from Colour::transparentBlack to Colours::white will produce a muddy grey colour midway, but Colour::transparentWhite to Colours::white will be white all the way across. @see ColourGradient */ ColourGradient (Colour colour1, float x1, float y1, Colour colour2, float x2, float y2, bool isRadial); /** Creates an uninitialised gradient. If you use this constructor instead of the other one, be sure to set all the object's public member variables before using it! */ ColourGradient() noexcept; /** Destructor */ ~ColourGradient(); //============================================================================== /** Removes any colours that have been added. This will also remove any start and end colours, so the gradient won't work. You'll need to add more colours with addColour(). */ void clearColours(); /** Adds a colour at a point along the length of the gradient. This allows the gradient to go through a spectrum of colours, instead of just a start and end colour. @param proportionAlongGradient a value between 0 and 1.0, which is the proportion of the distance along the line between the two points at which the colour should occur. @param colour the colour that should be used at this point @returns the index at which the new point was added */ int addColour (double proportionAlongGradient, Colour colour); /** Removes one of the colours from the gradient. */ void removeColour (int index); /** Multiplies the alpha value of all the colours by the given scale factor */ void multiplyOpacity (float multiplier) noexcept; //============================================================================== /** Returns the number of colour-stops that have been added. */ int getNumColours() const noexcept; /** Returns the position along the length of the gradient of the colour with this index. The index is from 0 to getNumColours() - 1. The return value will be between 0.0 and 1.0 */ double getColourPosition (int index) const noexcept; /** Returns the colour that was added with a given index. The index is from 0 to getNumColours() - 1. */ Colour getColour (int index) const noexcept; /** Changes the colour at a given index. The index is from 0 to getNumColours() - 1. */ void setColour (int index, Colour newColour) noexcept; /** Returns the an interpolated colour at any position along the gradient. @param position the position along the gradient, between 0 and 1 */ Colour getColourAtPosition (double position) const noexcept; //============================================================================== /** Creates a set of interpolated premultiplied ARGB values. This will resize the HeapBlock, fill it with the colours, and will return the number of colours that it added. When calling this, the ColourGradient must have at least 2 colour stops specified. */ int createLookupTable (const AffineTransform& transform, HeapBlock & resultLookupTable) const; /** Creates a set of interpolated premultiplied ARGB values. This will fill an array of a user-specified size with the gradient, interpolating to fit. The numEntries argument specifies the size of the array, and this size must be greater than zero. When calling this, the ColourGradient must have at least 2 colour stops specified. */ void createLookupTable (PixelARGB* resultLookupTable, int numEntries) const noexcept; /** Returns true if all colours are opaque. */ bool isOpaque() const noexcept; /** Returns true if all colours are completely transparent. */ bool isInvisible() const noexcept; //============================================================================== Point point1, point2; /** If true, the gradient should be filled circularly, centred around point1, with point2 defining a point on the circumference. If false, the gradient is linear between the two points. */ bool isRadial; bool operator== (const ColourGradient&) const noexcept; bool operator!= (const ColourGradient&) const noexcept; private: //============================================================================== struct ColourPoint { ColourPoint() noexcept {} ColourPoint (const double pos, Colour col) noexcept : position (pos), colour (col) {} bool operator== (const ColourPoint&) const noexcept; bool operator!= (const ColourPoint&) const noexcept; double position; Colour colour; }; Array colours; JUCE_LEAK_DETECTOR (ColourGradient) }; #endif // JUCE_COLOURGRADIENT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.cpp000066400000000000000000000346231320201440200310650ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ const Colour Colours::transparentBlack (0); const Colour Colours::transparentWhite (0x00ffffff); const Colour Colours::aliceblue (0xfff0f8ff); const Colour Colours::antiquewhite (0xfffaebd7); const Colour Colours::aqua (0xff00ffff); const Colour Colours::aquamarine (0xff7fffd4); const Colour Colours::azure (0xfff0ffff); const Colour Colours::beige (0xfff5f5dc); const Colour Colours::bisque (0xffffe4c4); const Colour Colours::black (0xff000000); const Colour Colours::blanchedalmond (0xffffebcd); const Colour Colours::blue (0xff0000ff); const Colour Colours::blueviolet (0xff8a2be2); const Colour Colours::brown (0xffa52a2a); const Colour Colours::burlywood (0xffdeb887); const Colour Colours::cadetblue (0xff5f9ea0); const Colour Colours::chartreuse (0xff7fff00); const Colour Colours::chocolate (0xffd2691e); const Colour Colours::coral (0xffff7f50); const Colour Colours::cornflowerblue (0xff6495ed); const Colour Colours::cornsilk (0xfffff8dc); const Colour Colours::crimson (0xffdc143c); const Colour Colours::cyan (0xff00ffff); const Colour Colours::darkblue (0xff00008b); const Colour Colours::darkcyan (0xff008b8b); const Colour Colours::darkgoldenrod (0xffb8860b); const Colour Colours::darkgrey (0xff555555); const Colour Colours::darkgreen (0xff006400); const Colour Colours::darkkhaki (0xffbdb76b); const Colour Colours::darkmagenta (0xff8b008b); const Colour Colours::darkolivegreen (0xff556b2f); const Colour Colours::darkorange (0xffff8c00); const Colour Colours::darkorchid (0xff9932cc); const Colour Colours::darkred (0xff8b0000); const Colour Colours::darksalmon (0xffe9967a); const Colour Colours::darkseagreen (0xff8fbc8f); const Colour Colours::darkslateblue (0xff483d8b); const Colour Colours::darkslategrey (0xff2f4f4f); const Colour Colours::darkturquoise (0xff00ced1); const Colour Colours::darkviolet (0xff9400d3); const Colour Colours::deeppink (0xffff1493); const Colour Colours::deepskyblue (0xff00bfff); const Colour Colours::dimgrey (0xff696969); const Colour Colours::dodgerblue (0xff1e90ff); const Colour Colours::firebrick (0xffb22222); const Colour Colours::floralwhite (0xfffffaf0); const Colour Colours::forestgreen (0xff228b22); const Colour Colours::fuchsia (0xffff00ff); const Colour Colours::gainsboro (0xffdcdcdc); const Colour Colours::gold (0xffffd700); const Colour Colours::goldenrod (0xffdaa520); const Colour Colours::grey (0xff808080); const Colour Colours::green (0xff008000); const Colour Colours::greenyellow (0xffadff2f); const Colour Colours::honeydew (0xfff0fff0); const Colour Colours::hotpink (0xffff69b4); const Colour Colours::indianred (0xffcd5c5c); const Colour Colours::indigo (0xff4b0082); const Colour Colours::ivory (0xfffffff0); const Colour Colours::khaki (0xfff0e68c); const Colour Colours::lavender (0xffe6e6fa); const Colour Colours::lavenderblush (0xfffff0f5); const Colour Colours::lemonchiffon (0xfffffacd); const Colour Colours::lightblue (0xffadd8e6); const Colour Colours::lightcoral (0xfff08080); const Colour Colours::lightcyan (0xffe0ffff); const Colour Colours::lightgoldenrodyellow (0xfffafad2); const Colour Colours::lightgreen (0xff90ee90); const Colour Colours::lightgrey (0xffd3d3d3); const Colour Colours::lightpink (0xffffb6c1); const Colour Colours::lightsalmon (0xffffa07a); const Colour Colours::lightseagreen (0xff20b2aa); const Colour Colours::lightskyblue (0xff87cefa); const Colour Colours::lightslategrey (0xff778899); const Colour Colours::lightsteelblue (0xffb0c4de); const Colour Colours::lightyellow (0xffffffe0); const Colour Colours::lime (0xff00ff00); const Colour Colours::limegreen (0xff32cd32); const Colour Colours::linen (0xfffaf0e6); const Colour Colours::magenta (0xffff00ff); const Colour Colours::maroon (0xff800000); const Colour Colours::mediumaquamarine (0xff66cdaa); const Colour Colours::mediumblue (0xff0000cd); const Colour Colours::mediumorchid (0xffba55d3); const Colour Colours::mediumpurple (0xff9370db); const Colour Colours::mediumseagreen (0xff3cb371); const Colour Colours::mediumslateblue (0xff7b68ee); const Colour Colours::mediumspringgreen (0xff00fa9a); const Colour Colours::mediumturquoise (0xff48d1cc); const Colour Colours::mediumvioletred (0xffc71585); const Colour Colours::midnightblue (0xff191970); const Colour Colours::mintcream (0xfff5fffa); const Colour Colours::mistyrose (0xffffe4e1); const Colour Colours::navajowhite (0xffffdead); const Colour Colours::navy (0xff000080); const Colour Colours::oldlace (0xfffdf5e6); const Colour Colours::olive (0xff808000); const Colour Colours::olivedrab (0xff6b8e23); const Colour Colours::orange (0xffffa500); const Colour Colours::orangered (0xffff4500); const Colour Colours::orchid (0xffda70d6); const Colour Colours::palegoldenrod (0xffeee8aa); const Colour Colours::palegreen (0xff98fb98); const Colour Colours::paleturquoise (0xffafeeee); const Colour Colours::palevioletred (0xffdb7093); const Colour Colours::papayawhip (0xffffefd5); const Colour Colours::peachpuff (0xffffdab9); const Colour Colours::peru (0xffcd853f); const Colour Colours::pink (0xffffc0cb); const Colour Colours::plum (0xffdda0dd); const Colour Colours::powderblue (0xffb0e0e6); const Colour Colours::purple (0xff800080); const Colour Colours::red (0xffff0000); const Colour Colours::rosybrown (0xffbc8f8f); const Colour Colours::royalblue (0xff4169e1); const Colour Colours::saddlebrown (0xff8b4513); const Colour Colours::salmon (0xfffa8072); const Colour Colours::sandybrown (0xfff4a460); const Colour Colours::seagreen (0xff2e8b57); const Colour Colours::seashell (0xfffff5ee); const Colour Colours::sienna (0xffa0522d); const Colour Colours::silver (0xffc0c0c0); const Colour Colours::skyblue (0xff87ceeb); const Colour Colours::slateblue (0xff6a5acd); const Colour Colours::slategrey (0xff708090); const Colour Colours::snow (0xfffffafa); const Colour Colours::springgreen (0xff00ff7f); const Colour Colours::steelblue (0xff4682b4); const Colour Colours::tan (0xffd2b48c); const Colour Colours::teal (0xff008080); const Colour Colours::thistle (0xffd8bfd8); const Colour Colours::tomato (0xffff6347); const Colour Colours::turquoise (0xff40e0d0); const Colour Colours::violet (0xffee82ee); const Colour Colours::wheat (0xfff5deb3); const Colour Colours::white (0xffffffff); const Colour Colours::whitesmoke (0xfff5f5f5); const Colour Colours::yellow (0xffffff00); const Colour Colours::yellowgreen (0xff9acd32); //============================================================================== Colour Colours::findColourForName (const String& colourName, Colour defaultColour) { static const uint32 presets[] = { // (first value is the string's hashcode, second is ARGB) 0x05978fff, 0xff000000, /* black */ 0x06bdcc29, 0xffffffff, /* white */ 0x002e305a, 0xff0000ff, /* blue */ 0x00308adf, 0xff808080, /* grey */ 0x05e0cf03, 0xff008000, /* green */ 0x0001b891, 0xffff0000, /* red */ 0xd43c6474, 0xffffff00, /* yellow */ 0x620886da, 0xfff0f8ff, /* aliceblue */ 0x20a2676a, 0xfffaebd7, /* antiquewhite */ 0x002dcebc, 0xff00ffff, /* aqua */ 0x46bb5f7e, 0xff7fffd4, /* aquamarine */ 0x0590228f, 0xfff0ffff, /* azure */ 0x05947fe4, 0xfff5f5dc, /* beige */ 0xad388e35, 0xffffe4c4, /* bisque */ 0x00674f7e, 0xffffebcd, /* blanchedalmond */ 0x39129959, 0xff8a2be2, /* blueviolet */ 0x059a8136, 0xffa52a2a, /* brown */ 0x89cea8f9, 0xffdeb887, /* burlywood */ 0x0fa260cf, 0xff5f9ea0, /* cadetblue */ 0x6b748956, 0xff7fff00, /* chartreuse */ 0x2903623c, 0xffd2691e, /* chocolate */ 0x05a74431, 0xffff7f50, /* coral */ 0x618d42dd, 0xff6495ed, /* cornflowerblue */ 0xe4b479fd, 0xfffff8dc, /* cornsilk */ 0x3d8c4edf, 0xffdc143c, /* crimson */ 0x002ed323, 0xff00ffff, /* cyan */ 0x67cc74d0, 0xff00008b, /* darkblue */ 0x67cd1799, 0xff008b8b, /* darkcyan */ 0x31bbd168, 0xffb8860b, /* darkgoldenrod */ 0x67cecf55, 0xff555555, /* darkgrey */ 0x920b194d, 0xff006400, /* darkgreen */ 0x923edd4c, 0xffbdb76b, /* darkkhaki */ 0x5c293873, 0xff8b008b, /* darkmagenta */ 0x6b6671fe, 0xff556b2f, /* darkolivegreen */ 0xbcfd2524, 0xffff8c00, /* darkorange */ 0xbcfdf799, 0xff9932cc, /* darkorchid */ 0x55ee0d5b, 0xff8b0000, /* darkred */ 0xc2e5f564, 0xffe9967a, /* darksalmon */ 0x61be858a, 0xff8fbc8f, /* darkseagreen */ 0xc2b0f2bd, 0xff483d8b, /* darkslateblue */ 0xc2b34d42, 0xff2f4f4f, /* darkslategrey */ 0x7cf2b06b, 0xff00ced1, /* darkturquoise */ 0xc8769375, 0xff9400d3, /* darkviolet */ 0x25832862, 0xffff1493, /* deeppink */ 0xfcad568f, 0xff00bfff, /* deepskyblue */ 0x634c8b67, 0xff696969, /* dimgrey */ 0x45c1ce55, 0xff1e90ff, /* dodgerblue */ 0xef19e3cb, 0xffb22222, /* firebrick */ 0xb852b195, 0xfffffaf0, /* floralwhite */ 0xd086fd06, 0xff228b22, /* forestgreen */ 0xe106b6d7, 0xffff00ff, /* fuchsia */ 0x7880d61e, 0xffdcdcdc, /* gainsboro */ 0x00308060, 0xffffd700, /* gold */ 0xb3b3bc1e, 0xffdaa520, /* goldenrod */ 0xbab8a537, 0xffadff2f, /* greenyellow */ 0xe4cacafb, 0xfff0fff0, /* honeydew */ 0x41892743, 0xffff69b4, /* hotpink */ 0xd5796f1a, 0xffcd5c5c, /* indianred */ 0xb969fed2, 0xff4b0082, /* indigo */ 0x05fef6a9, 0xfffffff0, /* ivory */ 0x06149302, 0xfff0e68c, /* khaki */ 0xad5a05c7, 0xffe6e6fa, /* lavender */ 0x7c4d5b99, 0xfffff0f5, /* lavenderblush */ 0x195756f0, 0xfffffacd, /* lemonchiffon */ 0x28e4ea70, 0xffadd8e6, /* lightblue */ 0xf3c7ccdb, 0xfff08080, /* lightcoral */ 0x28e58d39, 0xffe0ffff, /* lightcyan */ 0x21234e3c, 0xfffafad2, /* lightgoldenrodyellow */ 0xf40157ad, 0xff90ee90, /* lightgreen */ 0x28e744f5, 0xffd3d3d3, /* lightgrey */ 0x28eb3b8c, 0xffffb6c1, /* lightpink */ 0x9fb78304, 0xffffa07a, /* lightsalmon */ 0x50632b2a, 0xff20b2aa, /* lightseagreen */ 0x68fb7b25, 0xff87cefa, /* lightskyblue */ 0xa8a35ba2, 0xff778899, /* lightslategrey */ 0xa20d484f, 0xffb0c4de, /* lightsteelblue */ 0xaa2cf10a, 0xffffffe0, /* lightyellow */ 0x0032afd5, 0xff00ff00, /* lime */ 0x607bbc4e, 0xff32cd32, /* limegreen */ 0x06234efa, 0xfffaf0e6, /* linen */ 0x316858a9, 0xffff00ff, /* magenta */ 0xbf8ca470, 0xff800000, /* maroon */ 0xbd58e0b3, 0xff66cdaa, /* mediumaquamarine */ 0x967dfd4f, 0xff0000cd, /* mediumblue */ 0x056f5c58, 0xffba55d3, /* mediumorchid */ 0x07556b71, 0xff9370db, /* mediumpurple */ 0x5369b689, 0xff3cb371, /* mediumseagreen */ 0x066be19e, 0xff7b68ee, /* mediumslateblue */ 0x3256b281, 0xff00fa9a, /* mediumspringgreen */ 0xc0ad9f4c, 0xff48d1cc, /* mediumturquoise */ 0x628e63dd, 0xffc71585, /* mediumvioletred */ 0x168eb32a, 0xff191970, /* midnightblue */ 0x4306b960, 0xfff5fffa, /* mintcream */ 0x4cbc0e6b, 0xffffe4e1, /* mistyrose */ 0xe97218a6, 0xffffdead, /* navajowhite */ 0x00337bb6, 0xff000080, /* navy */ 0xadd2d33e, 0xfffdf5e6, /* oldlace */ 0x064ee1db, 0xff808000, /* olive */ 0x9e33a98a, 0xff6b8e23, /* olivedrab */ 0xc3de262e, 0xffffa500, /* orange */ 0x58bebba3, 0xffff4500, /* orangered */ 0xc3def8a3, 0xffda70d6, /* orchid */ 0x28cb4834, 0xffeee8aa, /* palegoldenrod */ 0x3d9dd619, 0xff98fb98, /* palegreen */ 0x74022737, 0xffafeeee, /* paleturquoise */ 0x15e2ebc8, 0xffdb7093, /* palevioletred */ 0x5fd898e2, 0xffffefd5, /* papayawhip */ 0x93e1b776, 0xffffdab9, /* peachpuff */ 0x003472f8, 0xffcd853f, /* peru */ 0x00348176, 0xffffc0cb, /* pink */ 0x00348d94, 0xffdda0dd, /* plum */ 0xd036be93, 0xffb0e0e6, /* powderblue */ 0xc5c507bc, 0xff800080, /* purple */ 0xa89d65b3, 0xffbc8f8f, /* rosybrown */ 0xbd9413e1, 0xff4169e1, /* royalblue */ 0xf456044f, 0xff8b4513, /* saddlebrown */ 0xc9c6f66e, 0xfffa8072, /* salmon */ 0x0bb131e1, 0xfff4a460, /* sandybrown */ 0x34636c14, 0xff2e8b57, /* seagreen */ 0x3507fb41, 0xfffff5ee, /* seashell */ 0xca348772, 0xffa0522d, /* sienna */ 0xca37d30d, 0xffc0c0c0, /* silver */ 0x80da74fb, 0xff87ceeb, /* skyblue */ 0x44a8dd73, 0xff6a5acd, /* slateblue */ 0x44ab37f8, 0xff708090, /* slategrey */ 0x0035f183, 0xfffffafa, /* snow */ 0xd5440d16, 0xff00ff7f, /* springgreen */ 0x3e1524a5, 0xff4682b4, /* steelblue */ 0x0001bfa1, 0xffd2b48c, /* tan */ 0x0036425c, 0xff008080, /* teal */ 0xafc8858f, 0xffd8bfd8, /* thistle */ 0xcc41600a, 0xffff6347, /* tomato */ 0xfeea9b21, 0xff40e0d0, /* turquoise */ 0xcf57947f, 0xffee82ee, /* violet */ 0x06bdbae7, 0xfff5deb3, /* wheat */ 0x10802ee6, 0xfff5f5f5, /* whitesmoke */ 0xe1b5130f, 0xff9acd32 /* yellowgreen */ }; const uint32 hash = (uint32) colourName.trim().toLowerCase().hashCode(); for (int i = 0; i < numElementsInArray (presets); i += 2) if (presets [i] == hash) return Colour (presets [i + 1]); return defaultColour; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.h000066400000000000000000000125071320201440200305270ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_COLOURS_H_INCLUDED #define JUCE_COLOURS_H_INCLUDED //============================================================================== /** Contains a set of predefined named colours (mostly standard HTML colours) @see Colour, Colours::greyLevel */ class Colours { public: static JUCE_API const Colour //============================================================================== transparentBlack, /**< ARGB = 0x00000000 */ transparentWhite, /**< ARGB = 0x00ffffff */ //============================================================================== black, /**< ARGB = 0xff000000 */ white, /**< ARGB = 0xffffffff */ blue, /**< ARGB = 0xff0000ff */ grey, /**< ARGB = 0xff808080 */ green, /**< ARGB = 0xff008000 */ red, /**< ARGB = 0xffff0000 */ yellow, /**< ARGB = 0xffffff00 */ //============================================================================== aliceblue, antiquewhite, aqua, aquamarine, azure, beige, bisque, blanchedalmond, blueviolet, brown, burlywood, cadetblue, chartreuse, chocolate, coral, cornflowerblue, cornsilk, crimson, cyan, darkblue, darkcyan, darkgoldenrod, darkgrey, darkgreen, darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred, darksalmon, darkseagreen, darkslateblue, darkslategrey, darkturquoise, darkviolet, deeppink, deepskyblue, dimgrey, dodgerblue, firebrick, floralwhite, forestgreen, fuchsia, gainsboro, gold, goldenrod, greenyellow, honeydew, hotpink, indianred, indigo, ivory, khaki, lavender, lavenderblush, lemonchiffon, lightblue, lightcoral, lightcyan, lightgoldenrodyellow, lightgreen, lightgrey, lightpink, lightsalmon, lightseagreen, lightskyblue, lightslategrey, lightsteelblue, lightyellow, lime, limegreen, linen, magenta, maroon, mediumaquamarine, mediumblue, mediumorchid, mediumpurple, mediumseagreen, mediumslateblue, mediumspringgreen, mediumturquoise, mediumvioletred, midnightblue, mintcream, mistyrose, navajowhite, navy, oldlace, olive, olivedrab, orange, orangered, orchid, palegoldenrod, palegreen, paleturquoise, palevioletred, papayawhip, peachpuff, peru, pink, plum, powderblue, purple, rosybrown, royalblue, saddlebrown, salmon, sandybrown, seagreen, seashell, sienna, silver, skyblue, slateblue, slategrey, snow, springgreen, steelblue, tan, teal, thistle, tomato, turquoise, violet, wheat, whitesmoke, yellowgreen; /** Attempts to look up a string in the list of known colour names, and return the appropriate colour. A non-case-sensitive search is made of the list of predefined colours, and if a match is found, that colour is returned. If no match is found, the colour passed in as the defaultColour parameter is returned. */ static JUCE_API Colour findColourForName (const String& colourName, Colour defaultColour); private: //============================================================================== // this isn't a class you should ever instantiate - it's just here for the // static values in it. Colours(); JUCE_DECLARE_NON_COPYABLE (Colours) }; #endif // JUCE_COLOURS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.cpp000066400000000000000000000075401320201440200311650ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ FillType::FillType() noexcept : colour (0xff000000) { } FillType::FillType (Colour c) noexcept : colour (c) { } FillType::FillType (const ColourGradient& gradient_) : colour (0xff000000), gradient (new ColourGradient (gradient_)) { } FillType::FillType (const Image& image_, const AffineTransform& transform_) noexcept : colour (0xff000000), image (image_), transform (transform_) { } FillType::FillType (const FillType& other) : colour (other.colour), gradient (other.gradient.createCopy()), image (other.image), transform (other.transform) { } FillType& FillType::operator= (const FillType& other) { if (this != &other) { colour = other.colour; gradient = other.gradient.createCopy(); image = other.image; transform = other.transform; } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS FillType::FillType (FillType&& other) noexcept : colour (other.colour), gradient (other.gradient.release()), image (static_cast (other.image)), transform (other.transform) { } FillType& FillType::operator= (FillType&& other) noexcept { jassert (this != &other); // hopefully the compiler should make this situation impossible! colour = other.colour; gradient = other.gradient.release(); image = static_cast (other.image); transform = other.transform; return *this; } #endif FillType::~FillType() noexcept { } bool FillType::operator== (const FillType& other) const { return colour == other.colour && image == other.image && transform == other.transform && (gradient == other.gradient || (gradient != nullptr && other.gradient != nullptr && *gradient == *other.gradient)); } bool FillType::operator!= (const FillType& other) const { return ! operator== (other); } void FillType::setColour (Colour newColour) noexcept { gradient = nullptr; image = Image::null; colour = newColour; } void FillType::setGradient (const ColourGradient& newGradient) { if (gradient != nullptr) { *gradient = newGradient; } else { image = Image::null; gradient = new ColourGradient (newGradient); colour = Colours::black; } } void FillType::setTiledImage (const Image& image_, const AffineTransform& transform_) noexcept { gradient = nullptr; image = image_; transform = transform_; colour = Colours::black; } void FillType::setOpacity (const float newOpacity) noexcept { colour = colour.withAlpha (newOpacity); } bool FillType::isInvisible() const noexcept { return colour.isTransparent() || (gradient != nullptr && gradient->isInvisible()); } FillType FillType::transformed (const AffineTransform& t) const { FillType f (*this); f.transform = f.transform.followedBy (t); return f; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.h000066400000000000000000000130321320201440200306230ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_FILLTYPE_H_INCLUDED #define JUCE_FILLTYPE_H_INCLUDED //============================================================================== /** Represents a colour or fill pattern to use for rendering paths. This is used by the Graphics and DrawablePath classes as a way to encapsulate a brush type. It can either be a solid colour, a gradient, or a tiled image. @see Graphics::setFillType, DrawablePath::setFill */ class JUCE_API FillType { public: //============================================================================== /** Creates a default fill type, of solid black. */ FillType() noexcept; /** Creates a fill type of a solid colour. @see setColour */ FillType (Colour colour) noexcept; /** Creates a gradient fill type. @see setGradient */ FillType (const ColourGradient& gradient); /** Creates a tiled image fill type. The transform allows you to set the scaling, offset and rotation of the pattern. @see setTiledImage */ FillType (const Image& image, const AffineTransform& transform) noexcept; /** Creates a copy of another FillType. */ FillType (const FillType&); /** Makes a copy of another FillType. */ FillType& operator= (const FillType&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS FillType (FillType&&) noexcept; FillType& operator= (FillType&&) noexcept; #endif /** Destructor. */ ~FillType() noexcept; //============================================================================== /** Returns true if this is a solid colour fill, and not a gradient or image. */ bool isColour() const noexcept { return gradient == nullptr && image.isNull(); } /** Returns true if this is a gradient fill. */ bool isGradient() const noexcept { return gradient != nullptr; } /** Returns true if this is a tiled image pattern fill. */ bool isTiledImage() const noexcept { return image.isValid(); } /** Turns this object into a solid colour fill. If the object was an image or gradient, those fields will no longer be valid. */ void setColour (Colour newColour) noexcept; /** Turns this object into a gradient fill. */ void setGradient (const ColourGradient& newGradient); /** Turns this object into a tiled image fill type. The transform allows you to set the scaling, offset and rotation of the pattern. */ void setTiledImage (const Image& image, const AffineTransform& transform) noexcept; /** Changes the opacity that should be used. If the fill is a solid colour, this just changes the opacity of that colour. For gradients and image tiles, it changes the opacity that will be used for them. */ void setOpacity (float newOpacity) noexcept; /** Returns the current opacity to be applied to the colour, gradient, or image. @see setOpacity */ float getOpacity() const noexcept { return colour.getFloatAlpha(); } /** Returns true if this fill type is completely transparent. */ bool isInvisible() const noexcept; /** Returns a copy of this fill, adding the specified transform applied to the existing transform. */ FillType transformed (const AffineTransform& transform) const; //============================================================================== /** The solid colour being used. If the fill type is not a solid colour, the alpha channel of this colour indicates the opacity that should be used for the fill, and the RGB channels are ignored. */ Colour colour; /** Returns the gradient that should be used for filling. This will be zero if the object is some other type of fill. If a gradient is active, the overall opacity with which it should be applied is indicated by the alpha channel of the colour variable. */ ScopedPointer gradient; /** The image that should be used for tiling. If an image fill is active, the overall opacity with which it should be applied is indicated by the alpha channel of the colour variable. */ Image image; /** The transform that should be applied to the image or gradient that's being drawn. */ AffineTransform transform; //============================================================================== bool operator== (const FillType&) const; bool operator!= (const FillType&) const; private: JUCE_LEAK_DETECTOR (FillType) }; #endif // JUCE_FILLTYPE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/colour/juce_PixelFormats.h000066400000000000000000000654721320201440200315270ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PIXELFORMATS_H_INCLUDED #define JUCE_PIXELFORMATS_H_INCLUDED //============================================================================== #if JUCE_MSVC #pragma pack (push, 1) #endif class PixelRGB; class PixelAlpha; inline uint32 maskPixelComponents (uint32 x) noexcept { return (x >> 8) & 0x00ff00ff; } inline uint32 clampPixelComponents (uint32 x) noexcept { return (x | (0x01000100 - maskPixelComponents (x))) & 0x00ff00ff; } //============================================================================== /** Represents a 32-bit INTERNAL pixel with premultiplied alpha, and can perform compositing operations with it. This is used internally by the imaging classes. @see PixelRGB */ class JUCE_API PixelARGB { public: /** Creates a pixel without defining its colour. */ PixelARGB() noexcept {} ~PixelARGB() noexcept {} PixelARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept { components.b = b; components.g = g; components.r = r; components.a = a; } //============================================================================== /** Returns a uint32 which represents the pixel in a platform dependent format. */ forcedinline uint32 getNativeARGB() const noexcept { return internal; } /** Returns a uint32 which will be in argb order as if constructed with the following mask operation ((alpha << 24) | (red << 16) | (green << 8) | blue). */ forcedinline uint32 getInARGBMaskOrder() const noexcept { #if JUCE_ANDROID return (uint32) ((components.a << 24) | (components.r << 16) | (components.g << 8) | (components.b << 0)); #else return getNativeARGB(); #endif } /** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ inline uint32 getInARGBMemoryOrder() const noexcept { #if JUCE_BIG_ENDIAN return getInARGBMaskOrder(); #else return (uint32) ((components.b << 24) | (components.g << 16) | (components.r << 8) | components.a); #endif } /** Return channels with an even index and insert zero bytes between them. This is useful for blending operations. The exact channels which are returned is platform dependent. */ forcedinline uint32 getEvenBytes() const noexcept { return 0x00ff00ff & internal; } /** Return channels with an odd index and insert zero bytes between them. This is useful for blending operations. The exact channels which are returned is platform dependent. */ forcedinline uint32 getOddBytes() const noexcept { return 0x00ff00ff & (internal >> 8); } //============================================================================== forcedinline uint8 getAlpha() const noexcept { return components.a; } forcedinline uint8 getRed() const noexcept { return components.r; } forcedinline uint8 getGreen() const noexcept { return components.g; } forcedinline uint8 getBlue() const noexcept { return components.b; } #if JUCE_GCC && ! JUCE_CLANG // NB these are here as a workaround because GCC refuses to bind to packed values. forcedinline uint8& getAlpha() noexcept { return comps [indexA]; } forcedinline uint8& getRed() noexcept { return comps [indexR]; } forcedinline uint8& getGreen() noexcept { return comps [indexG]; } forcedinline uint8& getBlue() noexcept { return comps [indexB]; } #else forcedinline uint8& getAlpha() noexcept { return components.a; } forcedinline uint8& getRed() noexcept { return components.r; } forcedinline uint8& getGreen() noexcept { return components.g; } forcedinline uint8& getBlue() noexcept { return components.b; } #endif //============================================================================== /** Copies another pixel colour over this one. This doesn't blend it - this colour is simply replaced by the other one. */ template forcedinline void set (const Pixel& src) noexcept { internal = src.getNativeARGB(); } //============================================================================== /** Sets the pixel's colour from individual components. */ void setARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept { components.b = b; components.g = g; components.r = r; components.a = a; } //============================================================================== /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends it accordingly. */ template forcedinline void blend (const Pixel& src) noexcept { uint32 rb = src.getEvenBytes(); uint32 ag = src.getOddBytes(); const uint32 alpha = 0x100 - (ag >> 16); rb += maskPixelComponents (getEvenBytes() * alpha); ag += maskPixelComponents (getOddBytes() * alpha); internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8); } /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends it accordingly. */ forcedinline void blend (const PixelRGB src) noexcept; /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before being used, so this can blend semi-transparently from a PixelRGB argument. */ template forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept { uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes()); uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes()); const uint32 alpha = 0x100 - (ag >> 16); rb += maskPixelComponents (getEvenBytes() * alpha); ag += maskPixelComponents (getOddBytes() * alpha); internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8); } /** Blends another pixel with this one, creating a colour that is somewhere between the two, as specified by the amount. */ template forcedinline void tween (const Pixel& src, const uint32 amount) noexcept { uint32 dEvenBytes = getEvenBytes(); dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8); dEvenBytes &= 0x00ff00ff; uint32 dOddBytes = getOddBytes(); dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8); dOddBytes &= 0x00ff00ff; dOddBytes <<= 8; dOddBytes |= dEvenBytes; internal = dOddBytes; } //============================================================================== /** Replaces the colour's alpha value with another one. */ forcedinline void setAlpha (const uint8 newAlpha) noexcept { components.a = newAlpha; } /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (int multiplier) noexcept { // increment alpha by 1, so that if multiplier == 255 (full alpha), // this function will not change the values. ++multiplier; internal = ((((uint32) multiplier) * getOddBytes()) & 0xff00ff00) | (((((uint32) multiplier) * getEvenBytes()) >> 8) & 0x00ff00ff); } forcedinline void multiplyAlpha (const float multiplier) noexcept { multiplyAlpha ((int) (multiplier * 255.0f)); } inline PixelARGB getUnpremultiplied() const noexcept { PixelARGB p (internal); p.unpremultiply(); return p; } /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() noexcept { const uint32 alpha = components.a; if (alpha < 0xff) { if (alpha == 0) { components.b = 0; components.g = 0; components.r = 0; } else { components.b = (uint8) ((components.b * alpha + 0x7f) >> 8); components.g = (uint8) ((components.g * alpha + 0x7f) >> 8); components.r = (uint8) ((components.r * alpha + 0x7f) >> 8); } } } /** Unpremultiplies the pixel's RGB values. */ forcedinline void unpremultiply() noexcept { const uint32 alpha = components.a; if (alpha < 0xff) { if (alpha == 0) { components.b = 0; components.g = 0; components.r = 0; } else { components.b = (uint8) jmin ((uint32) 0xffu, (components.b * 0xffu) / alpha); components.g = (uint8) jmin ((uint32) 0xffu, (components.g * 0xffu) / alpha); components.r = (uint8) jmin ((uint32) 0xffu, (components.r * 0xffu) / alpha); } } } forcedinline void desaturate() noexcept { if (components.a < 0xff && components.a > 0) { const int newUnpremultipliedLevel = (0xff * ((int) components.r + (int) components.g + (int) components.b) / (3 * components.a)); components.r = components.g = components.b = (uint8) ((newUnpremultipliedLevel * components.a + 0x7f) >> 8); } else { components.r = components.g = components.b = (uint8) (((int) components.r + (int) components.g + (int) components.b) / 3); } } //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ #if JUCE_ANDROID #if JUCE_BIG_ENDIAN enum { indexA = 0, indexR = 3, indexG = 2, indexB = 1 }; #else enum { indexA = 3, indexR = 0, indexG = 1, indexB = 2 }; #endif #else #if JUCE_BIG_ENDIAN enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; #else enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; #endif #endif private: //============================================================================== PixelARGB (const uint32 internalValue) noexcept : internal (internalValue) { } //============================================================================== struct Components { #if JUCE_ANDROID #if JUCE_BIG_ENDIAN uint8 a, b, g, r; #else uint8 r, g, b, a; #endif #else #if JUCE_BIG_ENDIAN uint8 a, r, g, b; #else uint8 b, g, r, a; #endif #endif } JUCE_PACKED; union { uint32 internal; Components components; #if JUCE_GCC uint8 comps[4]; // helper struct needed because gcc does not allow references to packed union members #endif }; } #ifndef DOXYGEN JUCE_PACKED #endif ; //============================================================================== /** Represents a 24-bit RGB pixel, and can perform compositing operations on it. This is used internally by the imaging classes. @see PixelARGB */ class JUCE_API PixelRGB { public: /** Creates a pixel without defining its colour. */ PixelRGB() noexcept {} ~PixelRGB() noexcept {} //============================================================================== /** Returns a uint32 which represents the pixel in a platform dependent format which is compatible with the native format of a PixelARGB. @see PixelARGB::getNativeARGB */ forcedinline uint32 getNativeARGB() const noexcept { #if JUCE_ANDROID return (uint32) ((0xff << 24) | r | (g << 8) | (b << 16)); #else return (uint32) ((0xff << 24) | b | (g << 8) | (r << 16)); #endif } /** Returns a uint32 which will be in argb order as if constructed with the following mask operation ((alpha << 24) | (red << 16) | (green << 8) | blue). */ forcedinline uint32 getInARGBMaskOrder() const noexcept { #if JUCE_ANDROID return (uint32) ((0xff << 24) | (r << 16) | (g << 8) | (b << 0)); #else return getNativeARGB(); #endif } /** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ inline uint32 getInARGBMemoryOrder() const noexcept { #if JUCE_BIG_ENDIAN return getInARGBMaskOrder(); #else return (uint32) ((b << 24) | (g << 16) | (r << 8) | 0xff); #endif } /** Return channels with an even index and insert zero bytes between them. This is useful for blending operations. The exact channels which are returned is platform dependent but compatible with the return value of getEvenBytes of the PixelARGB class. @see PixelARGB::getEvenBytes */ forcedinline uint32 getEvenBytes() const noexcept { #if JUCE_ANDROID return (uint32) (r | (b << 16)); #else return (uint32) (b | (r << 16)); #endif } /** Return channels with an odd index and insert zero bytes between them. This is useful for blending operations. The exact channels which are returned is platform dependent but compatible with the return value of getOddBytes of the PixelARGB class. @see PixelARGB::getOddBytes */ forcedinline uint32 getOddBytes() const noexcept { return (uint32)0xff0000 | g; } //============================================================================== forcedinline uint8 getAlpha() const noexcept { return 0xff; } forcedinline uint8 getRed() const noexcept { return r; } forcedinline uint8 getGreen() const noexcept { return g; } forcedinline uint8 getBlue() const noexcept { return b; } forcedinline uint8& getRed() noexcept { return r; } forcedinline uint8& getGreen() noexcept { return g; } forcedinline uint8& getBlue() noexcept { return b; } //============================================================================== /** Copies another pixel colour over this one. This doesn't blend it - this colour is simply replaced by the other one. Because PixelRGB has no alpha channel, any alpha value in the source pixel is thrown away. */ template forcedinline void set (const Pixel& src) noexcept { b = src.getBlue(); g = src.getGreen(); r = src.getRed(); } /** Sets the pixel's colour from individual components. */ void setARGB (const uint8, const uint8 red, const uint8 green, const uint8 blue) noexcept { r = red; g = green; b = blue; } //============================================================================== /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends it accordingly. */ template forcedinline void blend (const Pixel& src) noexcept { const uint32 alpha = (uint32) (0x100 - src.getAlpha()); // getEvenBytes returns 0x00rr00bb on non-android uint32 rb = clampPixelComponents (src.getEvenBytes() + maskPixelComponents (getEvenBytes() * alpha)); // getOddBytes returns 0x00aa00gg on non-android uint32 ag = clampPixelComponents (src.getOddBytes() + ((g * alpha) >> 8)); g = (uint8) (ag & 0xff); #if JUCE_ANDROID b = (uint8) (rb >> 16); r = (uint8) (rb & 0xff); #else r = (uint8) (rb >> 16); b = (uint8) (rb & 0xff); #endif } forcedinline void blend (const PixelRGB src) noexcept { set (src); } /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before being used, so this can blend semi-transparently from a PixelRGB argument. */ template forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept { uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes()); uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes()); const uint32 alpha = 0x100 - (ag >> 16); ag = clampPixelComponents (ag + (g * alpha >> 8)); rb = clampPixelComponents (rb + maskPixelComponents (getEvenBytes() * alpha)); g = (uint8) (ag & 0xff); #if JUCE_ANDROID b = (uint8) (rb >> 16); r = (uint8) (rb & 0xff); #else r = (uint8) (rb >> 16); b = (uint8) (rb & 0xff); #endif } /** Blends another pixel with this one, creating a colour that is somewhere between the two, as specified by the amount. */ template forcedinline void tween (const Pixel& src, const uint32 amount) noexcept { uint32 dEvenBytes = getEvenBytes(); dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8); uint32 dOddBytes = getOddBytes(); dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8); g = (uint8) (dOddBytes & 0xff); // dOddBytes = 0x00aa00gg #if JUCE_ANDROID r = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00bb00rr b = (uint8) (dEvenBytes >> 16); #else b = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00rr00bb r = (uint8) (dEvenBytes >> 16); #endif } //============================================================================== /** This method is included for compatibility with the PixelARGB class. */ forcedinline void setAlpha (const uint8) noexcept {} /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (int) noexcept {} /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (float) noexcept {} /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() noexcept {} /** Unpremultiplies the pixel's RGB values. */ forcedinline void unpremultiply() noexcept {} forcedinline void desaturate() noexcept { r = g = b = (uint8) (((int) r + (int) g + (int) b) / 3); } //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ #if JUCE_MAC enum { indexR = 0, indexG = 1, indexB = 2 }; #else enum { indexR = 2, indexG = 1, indexB = 0 }; #endif private: //============================================================================== PixelRGB (const uint32 internal) noexcept { #if JUCE_ANDROID b = (uint8) (internal >> 16); g = (uint8) (internal >> 8); r = (uint8) (internal); #else r = (uint8) (internal >> 16); g = (uint8) (internal >> 8); b = (uint8) (internal); #endif } //============================================================================== #if JUCE_MAC uint8 r, g, b; #else uint8 b, g, r; #endif } #ifndef DOXYGEN JUCE_PACKED #endif ; forcedinline void PixelARGB::blend (const PixelRGB src) noexcept { set (src); } //============================================================================== /** Represents an 8-bit single-channel pixel, and can perform compositing operations on it. This is used internally by the imaging classes. @see PixelARGB, PixelRGB */ class JUCE_API PixelAlpha { public: /** Creates a pixel without defining its colour. */ PixelAlpha() noexcept {} ~PixelAlpha() noexcept {} //============================================================================== /** Returns a uint32 which represents the pixel in a platform dependent format which is compatible with the native format of a PixelARGB. @see PixelARGB::getNativeARGB */ forcedinline uint32 getNativeARGB() const noexcept { return (uint32) ((a << 24) | (a << 16) | (a << 8) | a); } /** Returns a uint32 which will be in argb order as if constructed with the following mask operation ((alpha << 24) | (red << 16) | (green << 8) | blue). */ forcedinline uint32 getInARGBMaskOrder() const noexcept { return getNativeARGB(); } /** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ inline uint32 getInARGBMemoryOrder() const noexcept { return getNativeARGB(); } /** Return channels with an even index and insert zero bytes between them. This is useful for blending operations. The exact channels which are returned is platform dependent but compatible with the return value of getEvenBytes of the PixelARGB class. @see PixelARGB::getEvenBytes */ forcedinline uint32 getEvenBytes() const noexcept { return (uint32) ((a << 16) | a); } /** Return channels with an odd index and insert zero bytes between them. This is useful for blending operations. The exact channels which are returned is platform dependent but compatible with the return value of getOddBytes of the PixelARGB class. @see PixelARGB::getOddBytes */ forcedinline uint32 getOddBytes() const noexcept { return (uint32) ((a << 16) | a); } //============================================================================== forcedinline uint8 getAlpha() const noexcept { return a; } forcedinline uint8& getAlpha() noexcept { return a; } forcedinline uint8 getRed() const noexcept { return 0; } forcedinline uint8 getGreen() const noexcept { return 0; } forcedinline uint8 getBlue() const noexcept { return 0; } //============================================================================== /** Copies another pixel colour over this one. This doesn't blend it - this colour is simply replaced by the other one. */ template forcedinline void set (const Pixel& src) noexcept { a = src.getAlpha(); } /** Sets the pixel's colour from individual components. */ forcedinline void setARGB (const uint8 a_, const uint8 /*r*/, const uint8 /*g*/, const uint8 /*b*/) noexcept { a = a_; } //============================================================================== /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends it accordingly. */ template forcedinline void blend (const Pixel& src) noexcept { const int srcA = src.getAlpha(); a = (uint8) ((a * (0x100 - srcA) >> 8) + srcA); } /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before being used, so this can blend semi-transparently from a PixelRGB argument. */ template forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept { ++extraAlpha; const int srcAlpha = (int) ((extraAlpha * src.getAlpha()) >> 8); a = (uint8) ((a * (0x100 - srcAlpha) >> 8) + srcAlpha); } /** Blends another pixel with this one, creating a colour that is somewhere between the two, as specified by the amount. */ template forcedinline void tween (const Pixel& src, const uint32 amount) noexcept { a += ((src.getAlpha() - a) * amount) >> 8; } //============================================================================== /** Replaces the colour's alpha value with another one. */ forcedinline void setAlpha (const uint8 newAlpha) noexcept { a = newAlpha; } /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (int multiplier) noexcept { ++multiplier; a = (uint8) ((a * multiplier) >> 8); } forcedinline void multiplyAlpha (const float multiplier) noexcept { a = (uint8) (a * multiplier); } /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() noexcept {} /** Unpremultiplies the pixel's RGB values. */ forcedinline void unpremultiply() noexcept {} forcedinline void desaturate() noexcept {} //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ enum { indexA = 0 }; private: //============================================================================== PixelAlpha (const uint32 internal) noexcept { a = (uint8) (internal >> 24); } //============================================================================== uint8 a; } #ifndef DOXYGEN JUCE_PACKED #endif ; #if JUCE_MSVC #pragma pack (pop) #endif #endif // JUCE_PIXELFORMATS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/000077500000000000000000000000001320201440200262615ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp000066400000000000000000000533101320201440200331020ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace { template Rectangle coordsToRectangle (Type x, Type y, Type w, Type h) { #if JUCE_DEBUG const int maxVal = 0x3fffffff; jassert ((int) x >= -maxVal && (int) x <= maxVal && (int) y >= -maxVal && (int) y <= maxVal && (int) w >= 0 && (int) w <= maxVal && (int) h >= 0 && (int) h <= maxVal); #endif return Rectangle (x, y, w, h); } } //============================================================================== LowLevelGraphicsContext::LowLevelGraphicsContext() {} LowLevelGraphicsContext::~LowLevelGraphicsContext() {} //============================================================================== Graphics::Graphics (const Image& imageToDrawOnto) : context (*imageToDrawOnto.createLowLevelContext()), contextToDelete (&context), saveStatePending (false) { jassert (imageToDrawOnto.isValid()); // Can't draw into a null image! } Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept : context (internalContext), saveStatePending (false) { } Graphics::~Graphics() { } //============================================================================== void Graphics::resetToDefaultState() { saveStateIfPending(); context.setFill (FillType()); context.setFont (Font()); context.setInterpolationQuality (Graphics::mediumResamplingQuality); } bool Graphics::isVectorDevice() const { return context.isVectorDevice(); } bool Graphics::reduceClipRegion (const Rectangle& area) { saveStateIfPending(); return context.clipToRectangle (area); } bool Graphics::reduceClipRegion (const int x, const int y, const int w, const int h) { return reduceClipRegion (Rectangle (x, y, w, h)); } bool Graphics::reduceClipRegion (const RectangleList& clipRegion) { saveStateIfPending(); return context.clipToRectangleList (clipRegion); } bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform) { saveStateIfPending(); context.clipToPath (path, transform); return ! context.isClipEmpty(); } bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform) { saveStateIfPending(); context.clipToImageAlpha (image, transform); return ! context.isClipEmpty(); } void Graphics::excludeClipRegion (const Rectangle& rectangleToExclude) { saveStateIfPending(); context.excludeClipRectangle (rectangleToExclude); } bool Graphics::isClipEmpty() const { return context.isClipEmpty(); } Rectangle Graphics::getClipBounds() const { return context.getClipBounds(); } void Graphics::saveState() { saveStateIfPending(); saveStatePending = true; } void Graphics::restoreState() { if (saveStatePending) saveStatePending = false; else context.restoreState(); } void Graphics::saveStateIfPending() { if (saveStatePending) { saveStatePending = false; context.saveState(); } } void Graphics::setOrigin (Point newOrigin) { saveStateIfPending(); context.setOrigin (newOrigin); } void Graphics::setOrigin (int x, int y) { setOrigin (Point (x, y)); } void Graphics::addTransform (const AffineTransform& transform) { saveStateIfPending(); context.addTransform (transform); } bool Graphics::clipRegionIntersects (const Rectangle& area) const { return context.clipRegionIntersects (area); } void Graphics::beginTransparencyLayer (float layerOpacity) { saveStateIfPending(); context.beginTransparencyLayer (layerOpacity); } void Graphics::endTransparencyLayer() { context.endTransparencyLayer(); } //============================================================================== void Graphics::setColour (Colour newColour) { saveStateIfPending(); context.setFill (newColour); } void Graphics::setOpacity (const float newOpacity) { saveStateIfPending(); context.setOpacity (newOpacity); } void Graphics::setGradientFill (const ColourGradient& gradient) { setFillType (gradient); } void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity) { saveStateIfPending(); context.setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY))); context.setOpacity (opacity); } void Graphics::setFillType (const FillType& newFill) { saveStateIfPending(); context.setFill (newFill); } //============================================================================== void Graphics::setFont (const Font& newFont) { saveStateIfPending(); context.setFont (newFont); } void Graphics::setFont (const float newFontHeight) { setFont (context.getFont().withHeight (newFontHeight)); } Font Graphics::getCurrentFont() const { return context.getFont(); } //============================================================================== void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY, Justification justification) const { if (text.isNotEmpty()) { // Don't pass any vertical placement flags to this method - they'll be ignored. jassert (justification.getOnlyVerticalFlags() == 0); const int flags = justification.getOnlyHorizontalFlags(); if (flags == Justification::right) { if (startX < context.getClipBounds().getX()) return; } else if (flags == Justification::left) if (startX > context.getClipBounds().getRight()) return; GlyphArrangement arr; arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY); if (flags != Justification::left) { float w = arr.getBoundingBox (0, -1, true).getWidth(); if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0) w /= 2.0f; arr.draw (*this, AffineTransform::translation (-w, 0)); } else { arr.draw (*this); } } } void Graphics::drawMultiLineText (const String& text, const int startX, const int baselineY, const int maximumLineWidth) const { if (text.isNotEmpty() && startX < context.getClipBounds().getRight()) { GlyphArrangement arr; arr.addJustifiedText (context.getFont(), text, (float) startX, (float) baselineY, (float) maximumLineWidth, Justification::left); arr.draw (*this); } } void Graphics::drawText (const String& text, const Rectangle& area, Justification justificationType, bool useEllipsesIfTooBig) const { if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer())) { GlyphArrangement arr; arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f, area.getWidth(), useEllipsesIfTooBig); arr.justifyGlyphs (0, arr.getNumGlyphs(), area.getX(), area.getY(), area.getWidth(), area.getHeight(), justificationType); arr.draw (*this); } } void Graphics::drawText (const String& text, const Rectangle& area, Justification justificationType, bool useEllipsesIfTooBig) const { drawText (text, area.toFloat(), justificationType, useEllipsesIfTooBig); } void Graphics::drawText (const String& text, const int x, const int y, const int width, const int height, Justification justificationType, const bool useEllipsesIfTooBig) const { drawText (text, Rectangle (x, y, width, height), justificationType, useEllipsesIfTooBig); } void Graphics::drawFittedText (const String& text, const Rectangle& area, Justification justification, const int maximumNumberOfLines, const float minimumHorizontalScale) const { if (text.isNotEmpty() && (! area.isEmpty()) && context.clipRegionIntersects (area)) { GlyphArrangement arr; arr.addFittedText (context.getFont(), text, (float) area.getX(), (float) area.getY(), (float) area.getWidth(), (float) area.getHeight(), justification, maximumNumberOfLines, minimumHorizontalScale); arr.draw (*this); } } void Graphics::drawFittedText (const String& text, const int x, const int y, const int width, const int height, Justification justification, const int maximumNumberOfLines, const float minimumHorizontalScale) const { drawFittedText (text, coordsToRectangle (x, y, width, height), justification, maximumNumberOfLines, minimumHorizontalScale); } //============================================================================== void Graphics::fillRect (const Rectangle& r) const { context.fillRect (r, false); } void Graphics::fillRect (const Rectangle& r) const { context.fillRect (r); } void Graphics::fillRect (int x, int y, int width, int height) const { context.fillRect (coordsToRectangle (x, y, width, height), false); } void Graphics::fillRect (float x, float y, float width, float height) const { fillRect (coordsToRectangle (x, y, width, height)); } void Graphics::fillRectList (const RectangleList& rectangles) const { context.fillRectList (rectangles); } void Graphics::fillRectList (const RectangleList& rects) const { for (const Rectangle* r = rects.begin(), * const e = rects.end(); r != e; ++r) context.fillRect (*r, false); } void Graphics::setPixel (int x, int y) const { context.fillRect (Rectangle (x, y, 1, 1), false); } void Graphics::fillAll() const { fillRect (context.getClipBounds()); } void Graphics::fillAll (Colour colourToUse) const { if (! colourToUse.isTransparent()) { const Rectangle clip (context.getClipBounds()); context.saveState(); context.setFill (colourToUse); context.fillRect (clip, false); context.restoreState(); } } //============================================================================== void Graphics::fillPath (const Path& path, const AffineTransform& transform) const { if ((! context.isClipEmpty()) && ! path.isEmpty()) context.fillPath (path, transform); } void Graphics::strokePath (const Path& path, const PathStrokeType& strokeType, const AffineTransform& transform) const { Path stroke; strokeType.createStrokedPath (stroke, path, transform, context.getPhysicalPixelScaleFactor()); fillPath (stroke); } //============================================================================== void Graphics::drawRect (float x, float y, float width, float height, float lineThickness) const { drawRect (coordsToRectangle (x, y, width, height), lineThickness); } void Graphics::drawRect (int x, int y, int width, int height, int lineThickness) const { drawRect (coordsToRectangle (x, y, width, height), lineThickness); } void Graphics::drawRect (const Rectangle& r, int lineThickness) const { drawRect (r.toFloat(), (float) lineThickness); } void Graphics::drawRect (Rectangle r, const float lineThickness) const { jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f); RectangleList rects; rects.addWithoutMerging (r.removeFromTop (lineThickness)); rects.addWithoutMerging (r.removeFromBottom (lineThickness)); rects.addWithoutMerging (r.removeFromLeft (lineThickness)); rects.addWithoutMerging (r.removeFromRight (lineThickness)); context.fillRectList (rects); } //============================================================================== void Graphics::fillEllipse (const Rectangle& area) const { Path p; p.addEllipse (area); fillPath (p); } void Graphics::fillEllipse (float x, float y, float w, float h) const { fillEllipse (Rectangle (x, y, w, h)); } void Graphics::drawEllipse (float x, float y, float width, float height, float lineThickness) const { Path p; p.addEllipse (x, y, width, height); strokePath (p, PathStrokeType (lineThickness)); } void Graphics::drawEllipse (const Rectangle& area, float lineThickness) const { drawEllipse (area.getX(), area.getY(), area.getWidth(), area.getHeight(), lineThickness); } void Graphics::fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const { fillRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize); } void Graphics::fillRoundedRectangle (const Rectangle& r, const float cornerSize) const { Path p; p.addRoundedRectangle (r, cornerSize); fillPath (p); } void Graphics::drawRoundedRectangle (float x, float y, float width, float height, float cornerSize, float lineThickness) const { drawRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize, lineThickness); } void Graphics::drawRoundedRectangle (const Rectangle& r, float cornerSize, float lineThickness) const { Path p; p.addRoundedRectangle (r, cornerSize); strokePath (p, PathStrokeType (lineThickness)); } void Graphics::drawArrow (const Line& line, float lineThickness, float arrowheadWidth, float arrowheadLength) const { Path p; p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength); fillPath (p); } void Graphics::fillCheckerBoard (const Rectangle& area, const int checkWidth, const int checkHeight, Colour colour1, Colour colour2) const { jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less! if (checkWidth > 0 && checkHeight > 0) { context.saveState(); if (colour1 == colour2) { context.setFill (colour1); context.fillRect (area, false); } else { const Rectangle clipped (context.getClipBounds().getIntersection (area)); if (! clipped.isEmpty()) { context.clipToRectangle (clipped); const int checkNumX = (clipped.getX() - area.getX()) / checkWidth; const int checkNumY = (clipped.getY() - area.getY()) / checkHeight; const int startX = area.getX() + checkNumX * checkWidth; const int startY = area.getY() + checkNumY * checkHeight; const int right = clipped.getRight(); const int bottom = clipped.getBottom(); for (int i = 0; i < 2; ++i) { context.setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2); int cy = i; for (int y = startY; y < bottom; y += checkHeight) for (int x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2) context.fillRect (Rectangle (x, y, checkWidth, checkHeight), false); } } } context.restoreState(); } } //============================================================================== void Graphics::drawVerticalLine (const int x, float top, float bottom) const { if (top < bottom) context.fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } void Graphics::drawHorizontalLine (const int y, float left, float right) const { if (left < right) context.fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } void Graphics::drawLine (const Line& line) const { context.drawLine (line); } void Graphics::drawLine (float x1, float y1, float x2, float y2) const { context.drawLine (Line (x1, y1, x2, y2)); } void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThickness) const { drawLine (Line (x1, y1, x2, y2), lineThickness); } void Graphics::drawLine (const Line& line, const float lineThickness) const { Path p; p.addLineSegment (line, lineThickness); fillPath (p); } void Graphics::drawDashedLine (const Line& line, const float* const dashLengths, const int numDashLengths, const float lineThickness, int n) const { jassert (n >= 0 && n < numDashLengths); // your start index must be valid! const Point delta ((line.getEnd() - line.getStart()).toDouble()); const double totalLen = delta.getDistanceFromOrigin(); if (totalLen >= 0.1) { const double onePixAlpha = 1.0 / totalLen; for (double alpha = 0.0; alpha < 1.0;) { jassert (dashLengths[n] > 0); // can't have zero-length dashes! const double lastAlpha = alpha; alpha += dashLengths [n] * onePixAlpha; n = (n + 1) % numDashLengths; if ((n & 1) != 0) { const Line segment (line.getStart() + (delta * lastAlpha).toFloat(), line.getStart() + (delta * jmin (1.0, alpha)).toFloat()); if (lineThickness != 1.0f) drawLine (segment, lineThickness); else context.drawLine (segment); } } } } //============================================================================== void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality) { saveStateIfPending(); context.setInterpolationQuality (newQuality); } //============================================================================== void Graphics::drawImageAt (const Image& imageToDraw, int x, int y, bool fillAlphaChannel) const { drawImageTransformed (imageToDraw, AffineTransform::translation ((float) x, (float) y), fillAlphaChannel); } void Graphics::drawImageWithin (const Image& imageToDraw, int dx, int dy, int dw, int dh, RectanglePlacement placementWithinTarget, const bool fillAlphaChannelWithCurrentBrush) const { if (imageToDraw.isValid()) drawImageTransformed (imageToDraw, placementWithinTarget.getTransformToFit (imageToDraw.getBounds().toFloat(), coordsToRectangle (dx, dy, dw, dh).toFloat()), fillAlphaChannelWithCurrentBrush); } void Graphics::drawImage (const Image& imageToDraw, int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, const bool fillAlphaChannelWithCurrentBrush) const { if (imageToDraw.isValid() && context.clipRegionIntersects (coordsToRectangle (dx, dy, dw, dh))) drawImageTransformed (imageToDraw.getClippedImage (coordsToRectangle (sx, sy, sw, sh)), AffineTransform::scale (dw / (float) sw, dh / (float) sh) .translated ((float) dx, (float) dy), fillAlphaChannelWithCurrentBrush); } void Graphics::drawImageTransformed (const Image& imageToDraw, const AffineTransform& transform, const bool fillAlphaChannelWithCurrentBrush) const { if (imageToDraw.isValid() && ! context.isClipEmpty()) { if (fillAlphaChannelWithCurrentBrush) { context.saveState(); context.clipToImageAlpha (imageToDraw, transform); fillAll(); context.restoreState(); } else { context.drawImage (imageToDraw, transform); } } } //============================================================================== Graphics::ScopedSaveState::ScopedSaveState (Graphics& g) : context (g) { context.saveState(); } Graphics::ScopedSaveState::~ScopedSaveState() { context.restoreState(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.h000066400000000000000000001032211320201440200325440ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_GRAPHICSCONTEXT_H_INCLUDED #define JUCE_GRAPHICSCONTEXT_H_INCLUDED //============================================================================== /** A graphics context, used for drawing a component or image. When a Component needs painting, a Graphics context is passed to its Component::paint() method, and this you then call methods within this object to actually draw the component's content. A Graphics can also be created from an image, to allow drawing directly onto that image. @see Component::paint */ class JUCE_API Graphics { public: //============================================================================== /** Creates a Graphics object to draw directly onto the given image. The graphics object that is created will be set up to draw onto the image, with the context's clipping area being the entire size of the image, and its origin being the image's origin. To draw into a subsection of an image, use the reduceClipRegion() and setOrigin() methods. Obviously you shouldn't delete the image before this context is deleted. */ explicit Graphics (const Image& imageToDrawOnto); /** Destructor. */ ~Graphics(); //============================================================================== /** Changes the current drawing colour. This sets the colour that will now be used for drawing operations - it also sets the opacity to that of the colour passed-in. If a brush is being used when this method is called, the brush will be deselected, and any subsequent drawing will be done with a solid colour brush instead. @see setOpacity */ void setColour (Colour newColour); /** Changes the opacity to use with the current colour. If a solid colour is being used for drawing, this changes its opacity to this new value (i.e. it doesn't multiply the colour's opacity by this amount). If a gradient is being used, this will have no effect on it. A value of 0.0 is completely transparent, 1.0 is completely opaque. */ void setOpacity (float newOpacity); /** Sets the context to use a gradient for its fill pattern. */ void setGradientFill (const ColourGradient& gradient); /** Sets the context to use a tiled image pattern for filling. Make sure that you don't delete this image while it's still being used by this context! */ void setTiledImageFill (const Image& imageToUse, int anchorX, int anchorY, float opacity); /** Changes the current fill settings. @see setColour, setGradientFill, setTiledImageFill */ void setFillType (const FillType& newFill); //============================================================================== /** Changes the font to use for subsequent text-drawing functions. Note there's also a setFont (float, int) method to quickly change the size and style of the current font. @see drawSingleLineText, drawMultiLineText, drawText, drawFittedText */ void setFont (const Font& newFont); /** Changes the size of the currently-selected font. This is a convenient shortcut that changes the context's current font to a different size. The typeface won't be changed. @see Font */ void setFont (float newFontHeight); /** Returns the currently selected font. */ Font getCurrentFont() const; /** Draws a one-line text string. This will use the current colour (or brush) to fill the text. The font is the last one specified by setFont(). @param text the string to draw @param startX the position to draw the left-hand edge of the text @param baselineY the position of the text's baseline @param justification the horizontal flags indicate which end of the text string is anchored at the specified point. @see drawMultiLineText, drawText, drawFittedText, GlyphArrangement::addLineOfText */ void drawSingleLineText (const String& text, int startX, int baselineY, Justification justification = Justification::left) const; /** Draws text across multiple lines. This will break the text onto a new line where there's a new-line or carriage-return character, or at a word-boundary when the text becomes wider than the size specified by the maximumLineWidth parameter. @see setFont, drawSingleLineText, drawFittedText, GlyphArrangement::addJustifiedText */ void drawMultiLineText (const String& text, int startX, int baselineY, int maximumLineWidth) const; /** Draws a line of text within a specified rectangle. The text will be positioned within the rectangle based on the justification flags passed-in. If the string is too long to fit inside the rectangle, it will either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig flag is true). @see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText */ void drawText (const String& text, int x, int y, int width, int height, Justification justificationType, bool useEllipsesIfTooBig = true) const; /** Draws a line of text within a specified rectangle. The text will be positioned within the rectangle based on the justification flags passed-in. If the string is too long to fit inside the rectangle, it will either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig flag is true). @see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText */ void drawText (const String& text, const Rectangle& area, Justification justificationType, bool useEllipsesIfTooBig = true) const; /** Draws a line of text within a specified rectangle. The text will be positioned within the rectangle based on the justification flags passed-in. If the string is too long to fit inside the rectangle, it will either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig flag is true). @see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText */ void drawText (const String& text, const Rectangle& area, Justification justificationType, bool useEllipsesIfTooBig = true) const; /** Tries to draw a text string inside a given space. This does its best to make the given text readable within the specified rectangle, so it useful for labelling things. If the text is too big, it'll be squashed horizontally or broken over multiple lines if the maximumLinesToUse value allows this. If the text just won't fit into the space, it'll cram as much as possible in there, and put some ellipsis at the end to show that it's been truncated. A Justification parameter lets you specify how the text is laid out within the rectangle, both horizontally and vertically. The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you can set this value to 1.0f. Pass 0 if you want it to use a default value. @see GlyphArrangement::addFittedText */ void drawFittedText (const String& text, int x, int y, int width, int height, Justification justificationFlags, int maximumNumberOfLines, float minimumHorizontalScale = 0.0f) const; /** Tries to draw a text string inside a given space. This does its best to make the given text readable within the specified rectangle, so it useful for labelling things. If the text is too big, it'll be squashed horizontally or broken over multiple lines if the maximumLinesToUse value allows this. If the text just won't fit into the space, it'll cram as much as possible in there, and put some ellipsis at the end to show that it's been truncated. A Justification parameter lets you specify how the text is laid out within the rectangle, both horizontally and vertically. The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you can set this value to 1.0f. Pass 0 if you want it to use a default value. @see GlyphArrangement::addFittedText */ void drawFittedText (const String& text, const Rectangle& area, Justification justificationFlags, int maximumNumberOfLines, float minimumHorizontalScale = 0.0f) const; //============================================================================== /** Fills the context's entire clip region with the current colour or brush. (See also the fillAll (Colour) method which is a quick way of filling it with a given colour). */ void fillAll() const; /** Fills the context's entire clip region with a given colour. This leaves the context's current colour and brush unchanged, it just uses the specified colour temporarily. */ void fillAll (Colour colourToUse) const; //============================================================================== /** Fills a rectangle with the current colour or brush. @see drawRect, fillRoundedRectangle */ void fillRect (const Rectangle& rectangle) const; /** Fills a rectangle with the current colour or brush. @see drawRect, fillRoundedRectangle */ void fillRect (const Rectangle& rectangle) const; /** Fills a rectangle with the current colour or brush. @see drawRect, fillRoundedRectangle */ void fillRect (int x, int y, int width, int height) const; /** Fills a rectangle with the current colour or brush. @see drawRect, fillRoundedRectangle */ void fillRect (float x, float y, float width, float height) const; /** Fills a set of rectangles using the current colour or brush. If you have a lot of rectangles to draw, it may be more efficient to create a RectangleList and use this method than to call fillRect() multiple times. */ void fillRectList (const RectangleList& rectangles) const; /** Fills a set of rectangles using the current colour or brush. If you have a lot of rectangles to draw, it may be more efficient to create a RectangleList and use this method than to call fillRect() multiple times. */ void fillRectList (const RectangleList& rectangles) const; /** Uses the current colour or brush to fill a rectangle with rounded corners. @see drawRoundedRectangle, Path::addRoundedRectangle */ void fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const; /** Uses the current colour or brush to fill a rectangle with rounded corners. @see drawRoundedRectangle, Path::addRoundedRectangle */ void fillRoundedRectangle (const Rectangle& rectangle, float cornerSize) const; /** Fills a rectangle with a checkerboard pattern, alternating between two colours. */ void fillCheckerBoard (const Rectangle& area, int checkWidth, int checkHeight, Colour colour1, Colour colour2) const; /** Draws a rectangular outline, using the current colour or brush. The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards. @see fillRect */ void drawRect (int x, int y, int width, int height, int lineThickness = 1) const; /** Draws a rectangular outline, using the current colour or brush. The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards. @see fillRect */ void drawRect (float x, float y, float width, float height, float lineThickness = 1.0f) const; /** Draws a rectangular outline, using the current colour or brush. The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards. @see fillRect */ void drawRect (const Rectangle& rectangle, int lineThickness = 1) const; /** Draws a rectangular outline, using the current colour or brush. The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards. @see fillRect */ void drawRect (Rectangle rectangle, float lineThickness = 1.0f) const; /** Uses the current colour or brush to draw the outline of a rectangle with rounded corners. @see fillRoundedRectangle, Path::addRoundedRectangle */ void drawRoundedRectangle (float x, float y, float width, float height, float cornerSize, float lineThickness) const; /** Uses the current colour or brush to draw the outline of a rectangle with rounded corners. @see fillRoundedRectangle, Path::addRoundedRectangle */ void drawRoundedRectangle (const Rectangle& rectangle, float cornerSize, float lineThickness) const; /** Fills a 1x1 pixel using the current colour or brush. Note that because the context may be transformed, this is effectively the same as calling fillRect (x, y, 1, 1), and the actual result may involve multiple pixels. */ void setPixel (int x, int y) const; //============================================================================== /** Fills an ellipse with the current colour or brush. The ellipse is drawn to fit inside the given rectangle. @see drawEllipse, Path::addEllipse */ void fillEllipse (float x, float y, float width, float height) const; /** Fills an ellipse with the current colour or brush. The ellipse is drawn to fit inside the given rectangle. @see drawEllipse, Path::addEllipse */ void fillEllipse (const Rectangle& area) const; /** Draws an elliptical stroke using the current colour or brush. @see fillEllipse, Path::addEllipse */ void drawEllipse (float x, float y, float width, float height, float lineThickness) const; /** Draws an elliptical stroke using the current colour or brush. @see fillEllipse, Path::addEllipse */ void drawEllipse (const Rectangle& area, float lineThickness) const; //============================================================================== /** Draws a line between two points. The line is 1 pixel wide and drawn with the current colour or brush. TIP: If you're trying to draw horizontal or vertical lines, don't use this - it's better to use fillRect() instead unless you really need an angled line. */ void drawLine (float startX, float startY, float endX, float endY) const; /** Draws a line between two points with a given thickness. TIP: If you're trying to draw horizontal or vertical lines, don't use this - it's better to use fillRect() instead unless you really need an angled line. @see Path::addLineSegment */ void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const; /** Draws a line between two points. The line is 1 pixel wide and drawn with the current colour or brush. TIP: If you're trying to draw horizontal or vertical lines, don't use this - it's better to use fillRect() instead unless you really need an angled line. */ void drawLine (const Line& line) const; /** Draws a line between two points with a given thickness. @see Path::addLineSegment TIP: If you're trying to draw horizontal or vertical lines, don't use this - it's better to use fillRect() instead unless you really need an angled line. */ void drawLine (const Line& line, float lineThickness) const; /** Draws a dashed line using a custom set of dash-lengths. @param line the line to draw @param dashLengths a series of lengths to specify the on/off lengths - e.g. { 4, 5, 6, 7 } will draw a line of 4 pixels, skip 5 pixels, draw 6 pixels, skip 7 pixels, and then repeat. @param numDashLengths the number of elements in the array (this must be an even number). @param lineThickness the thickness of the line to draw @param dashIndexToStartFrom the index in the dash-length array to use for the first segment @see PathStrokeType::createDashedStroke */ void drawDashedLine (const Line& line, const float* dashLengths, int numDashLengths, float lineThickness = 1.0f, int dashIndexToStartFrom = 0) const; /** Draws a vertical line of pixels at a given x position. The x position is an integer, but the top and bottom of the line can be sub-pixel positions, and these will be anti-aliased if necessary. The bottom parameter must be greater than or equal to the top parameter. */ void drawVerticalLine (int x, float top, float bottom) const; /** Draws a horizontal line of pixels at a given y position. The y position is an integer, but the left and right ends of the line can be sub-pixel positions, and these will be anti-aliased if necessary. The right parameter must be greater than or equal to the left parameter. */ void drawHorizontalLine (int y, float left, float right) const; //============================================================================== /** Fills a path using the currently selected colour or brush. */ void fillPath (const Path& path, const AffineTransform& transform = AffineTransform::identity) const; /** Draws a path's outline using the currently selected colour or brush. */ void strokePath (const Path& path, const PathStrokeType& strokeType, const AffineTransform& transform = AffineTransform::identity) const; /** Draws a line with an arrowhead at its end. @param line the line to draw @param lineThickness the thickness of the line @param arrowheadWidth the width of the arrow head (perpendicular to the line) @param arrowheadLength the length of the arrow head (along the length of the line) */ void drawArrow (const Line& line, float lineThickness, float arrowheadWidth, float arrowheadLength) const; //============================================================================== /** Types of rendering quality that can be specified when drawing images. @see blendImage, Graphics::setImageResamplingQuality */ enum ResamplingQuality { lowResamplingQuality = 0, /**< Just uses a nearest-neighbour algorithm for resampling. */ mediumResamplingQuality = 1, /**< Uses bilinear interpolation for upsampling and area-averaging for downsampling. */ highResamplingQuality = 2 /**< Uses bicubic interpolation for upsampling and area-averaging for downsampling. */ }; /** Changes the quality that will be used when resampling images. By default a Graphics object will be set to mediumRenderingQuality. @see Graphics::drawImage, Graphics::drawImageTransformed, Graphics::drawImageWithin */ void setImageResamplingQuality (const ResamplingQuality newQuality); /** Draws an image. This will draw the whole of an image, positioning its top-left corner at the given coordinates, and keeping its size the same. This is the simplest image drawing method - the others give more control over the scaling and clipping of the images. Images are composited using the context's current opacity, so if you don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f) (or setColour() with an opaque colour) before drawing images. */ void drawImageAt (const Image& imageToDraw, int topLeftX, int topLeftY, bool fillAlphaChannelWithCurrentBrush = false) const; /** Draws part of an image, rescaling it to fit in a given target region. The specified area of the source image is rescaled and drawn to fill the specifed destination rectangle. Images are composited using the context's current opacity, so if you don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f) (or setColour() with an opaque colour) before drawing images. @param imageToDraw the image to overlay @param destX the left of the destination rectangle @param destY the top of the destination rectangle @param destWidth the width of the destination rectangle @param destHeight the height of the destination rectangle @param sourceX the left of the rectangle to copy from the source image @param sourceY the top of the rectangle to copy from the source image @param sourceWidth the width of the rectangle to copy from the source image @param sourceHeight the height of the rectangle to copy from the source image @param fillAlphaChannelWithCurrentBrush if true, then instead of drawing the source image's pixels, the source image's alpha channel is used as a mask with which to fill the destination using the current colour or brush. (If the source is has no alpha channel, then it will just fill the target with a solid rectangle) @see setImageResamplingQuality, drawImageAt, drawImageWithin, fillAlphaMap */ void drawImage (const Image& imageToDraw, int destX, int destY, int destWidth, int destHeight, int sourceX, int sourceY, int sourceWidth, int sourceHeight, bool fillAlphaChannelWithCurrentBrush = false) const; /** Draws an image, having applied an affine transform to it. This lets you throw the image around in some wacky ways, rotate it, shear, scale it, etc. Images are composited using the context's current opacity, so if you don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f) (or setColour() with an opaque colour) before drawing images. If fillAlphaChannelWithCurrentBrush is set to true, then the image's RGB channels are ignored and it is filled with the current brush, masked by its alpha channel. If you want to render only a subsection of an image, use Image::getClippedImage() to create the section that you need. @see setImageResamplingQuality, drawImage */ void drawImageTransformed (const Image& imageToDraw, const AffineTransform& transform, bool fillAlphaChannelWithCurrentBrush = false) const; /** Draws an image to fit within a designated rectangle. If the image is too big or too small for the space, it will be rescaled to fit as nicely as it can do without affecting its aspect ratio. It will then be placed within the target rectangle according to the justification flags specified. @param imageToDraw the source image to draw @param destX top-left of the target rectangle to fit it into @param destY top-left of the target rectangle to fit it into @param destWidth size of the target rectangle to fit the image into @param destHeight size of the target rectangle to fit the image into @param placementWithinTarget this specifies how the image should be positioned within the target rectangle - see the RectanglePlacement class for more details about this. @param fillAlphaChannelWithCurrentBrush if true, then instead of drawing the image, just its alpha channel will be used as a mask with which to draw with the current brush or colour. This is similar to fillAlphaMap(), and see also drawImage() @see setImageResamplingQuality, drawImage, drawImageTransformed, drawImageAt, RectanglePlacement */ void drawImageWithin (const Image& imageToDraw, int destX, int destY, int destWidth, int destHeight, RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush = false) const; //============================================================================== /** Returns the position of the bounding box for the current clipping region. @see getClipRegion, clipRegionIntersects */ Rectangle getClipBounds() const; /** Checks whether a rectangle overlaps the context's clipping region. If this returns false, no part of the given area can be drawn onto, so this method can be used to optimise a component's paint() method, by letting it avoid drawing complex objects that aren't within the region being repainted. */ bool clipRegionIntersects (const Rectangle& area) const; /** Intersects the current clipping region with another region. @returns true if the resulting clipping region is non-zero in size @see setOrigin, clipRegionIntersects */ bool reduceClipRegion (int x, int y, int width, int height); /** Intersects the current clipping region with another region. @returns true if the resulting clipping region is non-zero in size @see setOrigin, clipRegionIntersects */ bool reduceClipRegion (const Rectangle& area); /** Intersects the current clipping region with a rectangle list region. @returns true if the resulting clipping region is non-zero in size @see setOrigin, clipRegionIntersects */ bool reduceClipRegion (const RectangleList& clipRegion); /** Intersects the current clipping region with a path. @returns true if the resulting clipping region is non-zero in size @see reduceClipRegion */ bool reduceClipRegion (const Path& path, const AffineTransform& transform = AffineTransform::identity); /** Intersects the current clipping region with an image's alpha-channel. The current clipping path is intersected with the area covered by this image's alpha-channel, after the image has been transformed by the specified matrix. @param image the image whose alpha-channel should be used. If the image doesn't have an alpha-channel, it is treated as entirely opaque. @param transform a matrix to apply to the image @returns true if the resulting clipping region is non-zero in size @see reduceClipRegion */ bool reduceClipRegion (const Image& image, const AffineTransform& transform); /** Excludes a rectangle to stop it being drawn into. */ void excludeClipRegion (const Rectangle& rectangleToExclude); /** Returns true if no drawing can be done because the clip region is zero. */ bool isClipEmpty() const; //============================================================================== /** Saves the current graphics state on an internal stack. To restore the state, use restoreState(). @see ScopedSaveState */ void saveState(); /** Restores a graphics state that was previously saved with saveState(). @see ScopedSaveState */ void restoreState(); /** Uses RAII to save and restore the state of a graphics context. On construction, this calls Graphics::saveState(), and on destruction it calls Graphics::restoreState() on the Graphics object that you supply. */ class ScopedSaveState { public: ScopedSaveState (Graphics&); ~ScopedSaveState(); private: Graphics& context; JUCE_DECLARE_NON_COPYABLE (ScopedSaveState) }; //============================================================================== /** Begins rendering to an off-screen bitmap which will later be flattened onto the current context with the given opacity. The context uses an internal stack of temporary image layers to do this. When you've finished drawing to the layer, call endTransparencyLayer() to complete the operation and composite the finished layer. Every call to beginTransparencyLayer() MUST be matched by a corresponding call to endTransparencyLayer()! This call also saves the current state, and endTransparencyLayer() restores it. */ void beginTransparencyLayer (float layerOpacity); /** Completes a drawing operation to a temporary semi-transparent buffer. See beginTransparencyLayer() for more details. */ void endTransparencyLayer(); /** Moves the position of the context's origin. This changes the position that the context considers to be (0, 0) to the specified position. So if you call setOrigin with (100, 100), then the position that was previously referred to as (100, 100) will subsequently be considered to be (0, 0). @see reduceClipRegion, addTransform */ void setOrigin (Point newOrigin); /** Moves the position of the context's origin. This changes the position that the context considers to be (0, 0) to the specified position. So if you call setOrigin (100, 100), then the position that was previously referred to as (100, 100) will subsequently be considered to be (0, 0). @see reduceClipRegion, addTransform */ void setOrigin (int newOriginX, int newOriginY); /** Adds a transformation which will be performed on all the graphics operations that the context subsequently performs. After calling this, all the coordinates that are passed into the context will be transformed by this matrix. @see setOrigin */ void addTransform (const AffineTransform& transform); /** Resets the current colour, brush, and font to default settings. */ void resetToDefaultState(); /** Returns true if this context is drawing to a vector-based device, such as a printer. */ bool isVectorDevice() const; //============================================================================== /** Create a graphics that draws with a given low-level renderer. This method is intended for use only by people who know what they're doing. Note that the LowLevelGraphicsContext will NOT be deleted by this object. */ Graphics (LowLevelGraphicsContext&) noexcept; /** @internal */ LowLevelGraphicsContext& getInternalContext() const noexcept { return context; } private: //============================================================================== LowLevelGraphicsContext& context; ScopedPointer contextToDelete; bool saveStatePending; void saveStateIfPending(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Graphics) }; #endif // JUCE_GRAPHICSCONTEXT_H_INCLUDED juce_LowLevelGraphicsContext.h000066400000000000000000000101651320201440200341430ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_LOWLEVELGRAPHICSCONTEXT_H_INCLUDED #define JUCE_LOWLEVELGRAPHICSCONTEXT_H_INCLUDED //============================================================================== /** Interface class for graphics context objects, used internally by the Graphics class. Users are not supposed to create instances of this class directly - do your drawing via the Graphics object instead. It's a base class for different types of graphics context, that may perform software-based or OS-accelerated rendering. E.g. the LowLevelGraphicsSoftwareRenderer renders onto an image in memory, but other subclasses could render directly to a windows HDC, a Quartz context, or an OpenGL context. */ class JUCE_API LowLevelGraphicsContext { protected: //============================================================================== LowLevelGraphicsContext(); public: virtual ~LowLevelGraphicsContext(); /** Returns true if this device is vector-based, e.g. a printer. */ virtual bool isVectorDevice() const = 0; //============================================================================== /** Moves the origin to a new position. The coordinates are relative to the current origin, and indicate the new position of (0, 0). */ virtual void setOrigin (Point) = 0; virtual void addTransform (const AffineTransform&) = 0; virtual float getPhysicalPixelScaleFactor() = 0; virtual bool clipToRectangle (const Rectangle&) = 0; virtual bool clipToRectangleList (const RectangleList&) = 0; virtual void excludeClipRectangle (const Rectangle&) = 0; virtual void clipToPath (const Path&, const AffineTransform&) = 0; virtual void clipToImageAlpha (const Image&, const AffineTransform&) = 0; virtual bool clipRegionIntersects (const Rectangle&) = 0; virtual Rectangle getClipBounds() const = 0; virtual bool isClipEmpty() const = 0; virtual void saveState() = 0; virtual void restoreState() = 0; virtual void beginTransparencyLayer (float opacity) = 0; virtual void endTransparencyLayer() = 0; //============================================================================== virtual void setFill (const FillType&) = 0; virtual void setOpacity (float) = 0; virtual void setInterpolationQuality (Graphics::ResamplingQuality) = 0; //============================================================================== virtual void fillRect (const Rectangle&, bool replaceExistingContents) = 0; virtual void fillRect (const Rectangle&) = 0; virtual void fillRectList (const RectangleList&) = 0; virtual void fillPath (const Path&, const AffineTransform&) = 0; virtual void drawImage (const Image&, const AffineTransform&) = 0; virtual void drawLine (const Line&) = 0; virtual void setFont (const Font&) = 0; virtual const Font& getFont() = 0; virtual void drawGlyph (int glyphNumber, const AffineTransform&) = 0; virtual bool drawTextLayout (const AttributedString&, const Rectangle&) { return false; } }; #endif // JUCE_LOWLEVELGRAPHICSCONTEXT_H_INCLUDED juce_LowLevelGraphicsPostScriptRenderer.cpp000066400000000000000000000411261320201440200366540ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ // this will throw an assertion if you try to draw something that's not // possible in postscript #define WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS 0 //============================================================================== #if JUCE_DEBUG && WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS #define notPossibleInPostscriptAssert jassertfalse #else #define notPossibleInPostscriptAssert #endif //============================================================================== LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript, const String& documentTitle, const int totalWidth_, const int totalHeight_) : out (resultingPostScript), totalWidth (totalWidth_), totalHeight (totalHeight_), needToClip (true) { stateStack.add (new SavedState()); stateStack.getLast()->clip = Rectangle (totalWidth_, totalHeight_); const float scale = jmin ((520.0f / totalWidth_), (750.0f / totalHeight)); out << "%!PS-Adobe-3.0 EPSF-3.0" "\n%%BoundingBox: 0 0 600 824" "\n%%Pages: 0" "\n%%Creator: ROLI Ltd. JUCE" "\n%%Title: " << documentTitle << "\n%%CreationDate: none" "\n%%LanguageLevel: 2" "\n%%EndComments" "\n%%BeginProlog" "\n%%BeginResource: JRes" "\n/bd {bind def} bind def" "\n/c {setrgbcolor} bd" "\n/m {moveto} bd" "\n/l {lineto} bd" "\n/rl {rlineto} bd" "\n/ct {curveto} bd" "\n/cp {closepath} bd" "\n/pr {3 index 3 index moveto 1 index 0 rlineto 0 1 index rlineto pop neg 0 rlineto pop pop closepath} bd" "\n/doclip {initclip newpath} bd" "\n/endclip {clip newpath} bd" "\n%%EndResource" "\n%%EndProlog" "\n%%BeginSetup" "\n%%EndSetup" "\n%%Page: 1 1" "\n%%BeginPageSetup" "\n%%EndPageSetup\n\n" << "40 800 translate\n" << scale << ' ' << scale << " scale\n\n"; } LowLevelGraphicsPostScriptRenderer::~LowLevelGraphicsPostScriptRenderer() { } //============================================================================== bool LowLevelGraphicsPostScriptRenderer::isVectorDevice() const { return true; } void LowLevelGraphicsPostScriptRenderer::setOrigin (Point o) { if (! o.isOrigin()) { stateStack.getLast()->xOffset += o.x; stateStack.getLast()->yOffset += o.y; needToClip = true; } } void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /*transform*/) { //xxx jassertfalse; } float LowLevelGraphicsPostScriptRenderer::getPhysicalPixelScaleFactor() { return 1.0f; } bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle& r) { needToClip = true; return stateStack.getLast()->clip.clipTo (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset)); } bool LowLevelGraphicsPostScriptRenderer::clipToRectangleList (const RectangleList& clipRegion) { needToClip = true; return stateStack.getLast()->clip.clipTo (clipRegion); } void LowLevelGraphicsPostScriptRenderer::excludeClipRectangle (const Rectangle& r) { needToClip = true; stateStack.getLast()->clip.subtract (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset)); } void LowLevelGraphicsPostScriptRenderer::clipToPath (const Path& path, const AffineTransform& transform) { writeClip(); Path p (path); p.applyTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset)); writePath (p); out << "clip\n"; } void LowLevelGraphicsPostScriptRenderer::clipToImageAlpha (const Image& /*sourceImage*/, const AffineTransform& /*transform*/) { needToClip = true; jassertfalse; // xxx } bool LowLevelGraphicsPostScriptRenderer::clipRegionIntersects (const Rectangle& r) { return stateStack.getLast()->clip.intersectsRectangle (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset)); } Rectangle LowLevelGraphicsPostScriptRenderer::getClipBounds() const { return stateStack.getLast()->clip.getBounds().translated (-stateStack.getLast()->xOffset, -stateStack.getLast()->yOffset); } bool LowLevelGraphicsPostScriptRenderer::isClipEmpty() const { return stateStack.getLast()->clip.isEmpty(); } //============================================================================== LowLevelGraphicsPostScriptRenderer::SavedState::SavedState() : xOffset (0), yOffset (0) { } LowLevelGraphicsPostScriptRenderer::SavedState::~SavedState() { } void LowLevelGraphicsPostScriptRenderer::saveState() { stateStack.add (new SavedState (*stateStack.getLast())); } void LowLevelGraphicsPostScriptRenderer::restoreState() { jassert (stateStack.size() > 0); if (stateStack.size() > 0) stateStack.removeLast(); } void LowLevelGraphicsPostScriptRenderer::beginTransparencyLayer (float) { } void LowLevelGraphicsPostScriptRenderer::endTransparencyLayer() { } //============================================================================== void LowLevelGraphicsPostScriptRenderer::writeClip() { if (needToClip) { needToClip = false; out << "doclip "; int itemsOnLine = 0; for (const Rectangle* i = stateStack.getLast()->clip.begin(), * const e = stateStack.getLast()->clip.end(); i != e; ++i) { if (++itemsOnLine == 6) { itemsOnLine = 0; out << '\n'; } out << i->getX() << ' ' << -i->getY() << ' ' << i->getWidth() << ' ' << -i->getHeight() << " pr "; } out << "endclip\n"; } } void LowLevelGraphicsPostScriptRenderer::writeColour (Colour colour) { Colour c (Colours::white.overlaidWith (colour)); if (lastColour != c) { lastColour = c; out << String (c.getFloatRed(), 3) << ' ' << String (c.getFloatGreen(), 3) << ' ' << String (c.getFloatBlue(), 3) << " c\n"; } } void LowLevelGraphicsPostScriptRenderer::writeXY (const float x, const float y) const { out << String (x, 2) << ' ' << String (-y, 2) << ' '; } void LowLevelGraphicsPostScriptRenderer::writePath (const Path& path) const { out << "newpath "; float lastX = 0.0f; float lastY = 0.0f; int itemsOnLine = 0; Path::Iterator i (path); while (i.next()) { if (++itemsOnLine == 4) { itemsOnLine = 0; out << '\n'; } switch (i.elementType) { case Path::Iterator::startNewSubPath: writeXY (i.x1, i.y1); lastX = i.x1; lastY = i.y1; out << "m "; break; case Path::Iterator::lineTo: writeXY (i.x1, i.y1); lastX = i.x1; lastY = i.y1; out << "l "; break; case Path::Iterator::quadraticTo: { const float cp1x = lastX + (i.x1 - lastX) * 2.0f / 3.0f; const float cp1y = lastY + (i.y1 - lastY) * 2.0f / 3.0f; const float cp2x = cp1x + (i.x2 - lastX) / 3.0f; const float cp2y = cp1y + (i.y2 - lastY) / 3.0f; writeXY (cp1x, cp1y); writeXY (cp2x, cp2y); writeXY (i.x2, i.y2); out << "ct "; lastX = i.x2; lastY = i.y2; } break; case Path::Iterator::cubicTo: writeXY (i.x1, i.y1); writeXY (i.x2, i.y2); writeXY (i.x3, i.y3); out << "ct "; lastX = i.x3; lastY = i.y3; break; case Path::Iterator::closePath: out << "cp "; break; default: jassertfalse; break; } } out << '\n'; } void LowLevelGraphicsPostScriptRenderer::writeTransform (const AffineTransform& trans) const { out << "[ " << trans.mat00 << ' ' << trans.mat10 << ' ' << trans.mat01 << ' ' << trans.mat11 << ' ' << trans.mat02 << ' ' << trans.mat12 << " ] concat "; } //============================================================================== void LowLevelGraphicsPostScriptRenderer::setFill (const FillType& fillType) { stateStack.getLast()->fillType = fillType; } void LowLevelGraphicsPostScriptRenderer::setOpacity (float /*opacity*/) { } void LowLevelGraphicsPostScriptRenderer::setInterpolationQuality (Graphics::ResamplingQuality /*quality*/) { } //============================================================================== void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle& r, const bool /*replaceExistingContents*/) { fillRect (r.toFloat()); } void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle& r) { if (stateStack.getLast()->fillType.isColour()) { writeClip(); writeColour (stateStack.getLast()->fillType.colour); Rectangle r2 (r.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset)); out << r2.getX() << ' ' << -r2.getBottom() << ' ' << r2.getWidth() << ' ' << r2.getHeight() << " rectfill\n"; } else { Path p; p.addRectangle (r); fillPath (p, AffineTransform::identity); } } void LowLevelGraphicsPostScriptRenderer::fillRectList (const RectangleList& list) { fillPath (list.toPath(), AffineTransform::identity); } //============================================================================== void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t) { if (stateStack.getLast()->fillType.isColour()) { writeClip(); Path p (path); p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset)); writePath (p); writeColour (stateStack.getLast()->fillType.colour); out << "fill\n"; } else if (stateStack.getLast()->fillType.isGradient()) { // this doesn't work correctly yet - it could be improved to handle solid gradients, but // postscript can't do semi-transparent ones. notPossibleInPostscriptAssert; // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file writeClip(); out << "gsave "; { Path p (path); p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset)); writePath (p); out << "clip\n"; } const Rectangle bounds (stateStack.getLast()->clip.getBounds()); // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the // time-being, this just fills it with the average colour.. writeColour (stateStack.getLast()->fillType.gradient->getColourAtPosition (0.5f)); out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n"; out << "grestore\n"; } } //============================================================================== void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, const int sx, const int sy, const int maxW, const int maxH) const { out << "{<\n"; const int w = jmin (maxW, im.getWidth()); const int h = jmin (maxH, im.getHeight()); int charsOnLine = 0; const Image::BitmapData srcData (im, 0, 0, w, h); Colour pixel; for (int y = h; --y >= 0;) { for (int x = 0; x < w; ++x) { const uint8* pixelData = srcData.getPixelPointer (x, y); if (x >= sx && y >= sy) { if (im.isARGB()) { PixelARGB p (*(const PixelARGB*) pixelData); p.unpremultiply(); pixel = Colours::white.overlaidWith (Colour (p)); } else if (im.isRGB()) { pixel = Colour (*((const PixelRGB*) pixelData)); } else { pixel = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixelData); } } else { pixel = Colours::transparentWhite; } const uint8 pixelValues[3] = { pixel.getRed(), pixel.getGreen(), pixel.getBlue() }; out << String::toHexString (pixelValues, 3, 0); charsOnLine += 3; if (charsOnLine > 100) { out << '\n'; charsOnLine = 0; } } } out << "\n>}\n"; } void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform) { const int w = sourceImage.getWidth(); const int h = sourceImage.getHeight(); writeClip(); out << "gsave "; writeTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset) .scaled (1.0f, -1.0f)); RectangleList imageClip; sourceImage.createSolidAreaMask (imageClip, 0.5f); out << "newpath "; int itemsOnLine = 0; for (const Rectangle* i = imageClip.begin(), * const e = imageClip.end(); i != e; ++i) { if (++itemsOnLine == 6) { out << '\n'; itemsOnLine = 0; } out << i->getX() << ' ' << i->getY() << ' ' << i->getWidth() << ' ' << i->getHeight() << " pr "; } out << " clip newpath\n"; out << w << ' ' << h << " scale\n"; out << w << ' ' << h << " 8 [" << w << " 0 0 -" << h << ' ' << (int) 0 << ' ' << h << " ]\n"; writeImage (sourceImage, 0, 0, w, h); out << "false 3 colorimage grestore\n"; needToClip = true; } //============================================================================== void LowLevelGraphicsPostScriptRenderer::drawLine (const Line & line) { Path p; p.addLineSegment (line, 1.0f); fillPath (p, AffineTransform::identity); } //============================================================================== void LowLevelGraphicsPostScriptRenderer::setFont (const Font& newFont) { stateStack.getLast()->font = newFont; } const Font& LowLevelGraphicsPostScriptRenderer::getFont() { return stateStack.getLast()->font; } void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) { Path p; Font& font = stateStack.getLast()->font; font.getTypeface()->getOutlineForGlyph (glyphNumber, p); fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform)); } juce_LowLevelGraphicsPostScriptRenderer.h000066400000000000000000000110311320201440200363110ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_H_INCLUDED #define JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_H_INCLUDED //============================================================================== /** An implementation of LowLevelGraphicsContext that turns the drawing operations into a PostScript document. */ class JUCE_API LowLevelGraphicsPostScriptRenderer : public LowLevelGraphicsContext { public: //============================================================================== LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript, const String& documentTitle, int totalWidth, int totalHeight); ~LowLevelGraphicsPostScriptRenderer(); //============================================================================== bool isVectorDevice() const override; void setOrigin (Point) override; void addTransform (const AffineTransform&) override; float getPhysicalPixelScaleFactor() override; bool clipToRectangle (const Rectangle&) override; bool clipToRectangleList (const RectangleList&) override; void excludeClipRectangle (const Rectangle&) override; void clipToPath (const Path&, const AffineTransform&) override; void clipToImageAlpha (const Image&, const AffineTransform&) override; void saveState() override; void restoreState() override; void beginTransparencyLayer (float) override; void endTransparencyLayer() override; bool clipRegionIntersects (const Rectangle&) override; Rectangle getClipBounds() const override; bool isClipEmpty() const override; //============================================================================== void setFill (const FillType&) override; void setOpacity (float) override; void setInterpolationQuality (Graphics::ResamplingQuality) override; //============================================================================== void fillRect (const Rectangle&, bool replaceExistingContents) override; void fillRect (const Rectangle&) override; void fillRectList (const RectangleList&) override; void fillPath (const Path&, const AffineTransform&) override; void drawImage (const Image&, const AffineTransform&) override; void drawLine (const Line &) override; //============================================================================== const Font& getFont() override; void setFont (const Font&) override; void drawGlyph (int glyphNumber, const AffineTransform&) override; protected: //============================================================================== OutputStream& out; int totalWidth, totalHeight; bool needToClip; Colour lastColour; struct SavedState { SavedState(); ~SavedState(); RectangleList clip; int xOffset, yOffset; FillType fillType; Font font; private: SavedState& operator= (const SavedState&); }; OwnedArray stateStack; void writeClip(); void writeColour (Colour colour); void writePath (const Path&) const; void writeXY (float x, float y) const; void writeTransform (const AffineTransform&) const; void writeImage (const Image&, int sx, int sy, int maxW, int maxH) const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsPostScriptRenderer) }; #endif // JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_H_INCLUDED juce_LowLevelGraphicsSoftwareRenderer.cpp000066400000000000000000000032601320201440200363310ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image) : RenderingHelpers::StackBasedLowLevelGraphicsContext (new RenderingHelpers::SoftwareRendererSavedState (image, image.getBounds())) { } LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, Point origin, const RectangleList& initialClip) : RenderingHelpers::StackBasedLowLevelGraphicsContext (new RenderingHelpers::SoftwareRendererSavedState (image, initialClip, origin)) { } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() {} juce_LowLevelGraphicsSoftwareRenderer.h000066400000000000000000000043341320201440200360010ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/contexts/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_H_INCLUDED #define JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_H_INCLUDED //============================================================================== /** A lowest-common-denominator implementation of LowLevelGraphicsContext that does all its rendering in memory. User code is not supposed to create instances of this class directly - do all your rendering via the Graphics class instead. */ class JUCE_API LowLevelGraphicsSoftwareRenderer : public RenderingHelpers::StackBasedLowLevelGraphicsContext { public: //============================================================================== /** Creates a context to render into an image. */ LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOnto); /** Creates a context to render into a clipped subsection of an image. */ LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOnto, Point origin, const RectangleList& initialClip); /** Destructor. */ ~LowLevelGraphicsSoftwareRenderer(); private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsSoftwareRenderer) }; #endif // JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/effects/000077500000000000000000000000001320201440200260315ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.cpp000066400000000000000000000144271320201440200327420ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ static inline void blurDataTriplets (uint8* d, int num, const int delta) noexcept { uint32 last = d[0]; d[0] = (uint8) ((d[0] + d[delta] + 1) / 3); d += delta; num -= 2; do { const uint32 newLast = d[0]; d[0] = (uint8) ((last + d[0] + d[delta] + 1) / 3); d += delta; last = newLast; } while (--num > 0); d[0] = (uint8) ((last + d[0] + 1) / 3); } static void blurSingleChannelImage (uint8* const data, const int width, const int height, const int lineStride, const int repetitions) noexcept { jassert (width > 2 && height > 2); for (int y = 0; y < height; ++y) for (int i = repetitions; --i >= 0;) blurDataTriplets (data + lineStride * y, width, 1); for (int x = 0; x < width; ++x) for (int i = repetitions; --i >= 0;) blurDataTriplets (data + x, height, lineStride); } static void blurSingleChannelImage (Image& image, int radius) { const Image::BitmapData bm (image, Image::BitmapData::readWrite); blurSingleChannelImage (bm.data, bm.width, bm.height, bm.lineStride, 2 * radius); } //============================================================================== DropShadow::DropShadow() noexcept : colour (0x90000000), radius (4) { } DropShadow::DropShadow (Colour shadowColour, const int r, Point o) noexcept : colour (shadowColour), radius (r), offset (o) { jassert (radius > 0); } void DropShadow::drawForImage (Graphics& g, const Image& srcImage) const { jassert (radius > 0); if (srcImage.isValid()) { Image shadowImage (srcImage.convertedToFormat (Image::SingleChannel)); shadowImage.duplicateIfShared(); blurSingleChannelImage (shadowImage, radius); g.setColour (colour); g.drawImageAt (shadowImage, offset.x, offset.y, true); } } void DropShadow::drawForPath (Graphics& g, const Path& path) const { jassert (radius > 0); const Rectangle area ((path.getBounds().getSmallestIntegerContainer() + offset) .expanded (radius + 1) .getIntersection (g.getClipBounds().expanded (radius + 1))); if (area.getWidth() > 2 && area.getHeight() > 2) { Image renderedPath (Image::SingleChannel, area.getWidth(), area.getHeight(), true); { Graphics g2 (renderedPath); g2.setColour (Colours::white); g2.fillPath (path, AffineTransform::translation ((float) (offset.x - area.getX()), (float) (offset.y - area.getY()))); } blurSingleChannelImage (renderedPath, radius); g.setColour (colour); g.drawImageAt (renderedPath, area.getX(), area.getY(), true); } } static void drawShadowSection (Graphics& g, ColourGradient& cg, Rectangle area, bool isCorner, float centreX, float centreY, float edgeX, float edgeY) { cg.point1 = area.getRelativePoint (centreX, centreY); cg.point2 = area.getRelativePoint (edgeX, edgeY); cg.isRadial = isCorner; g.setGradientFill (cg); g.fillRect (area); } void DropShadow::drawForRectangle (Graphics& g, const Rectangle& targetArea) const { ColourGradient cg (colour, 0, 0, colour.withAlpha (0.0f), 0, 0, false); for (float i = 0.05f; i < 1.0f; i += 0.1f) cg.addColour (1.0 - i, colour.withMultipliedAlpha (i * i)); const float radiusInset = (radius + 1) / 2.0f; const float expandedRadius = radius + radiusInset; const Rectangle area (targetArea.toFloat().reduced (radiusInset) + offset.toFloat()); Rectangle r (area.expanded (expandedRadius)); Rectangle top (r.removeFromTop (expandedRadius)); Rectangle bottom (r.removeFromBottom (expandedRadius)); drawShadowSection (g, cg, top.removeFromLeft (expandedRadius), true, 1.0f, 1.0f, 0, 1.0f); drawShadowSection (g, cg, top.removeFromRight (expandedRadius), true, 0, 1.0f, 1.0f, 1.0f); drawShadowSection (g, cg, top, false, 0, 1.0f, 0, 0); drawShadowSection (g, cg, bottom.removeFromLeft (expandedRadius), true, 1.0f, 0, 0, 0); drawShadowSection (g, cg, bottom.removeFromRight (expandedRadius), true, 0, 0, 1.0f, 0); drawShadowSection (g, cg, bottom, false, 0, 0, 0, 1.0f); drawShadowSection (g, cg, r.removeFromLeft (expandedRadius), false, 1.0f, 0, 0, 0); drawShadowSection (g, cg, r.removeFromRight (expandedRadius), false, 0, 0, 1.0f, 0); g.setColour (colour); g.fillRect (area); } //============================================================================== DropShadowEffect::DropShadowEffect() {} DropShadowEffect::~DropShadowEffect() {} void DropShadowEffect::setShadowProperties (const DropShadow& newShadow) { shadow = newShadow; } void DropShadowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) { DropShadow s (shadow); s.radius = roundToInt (s.radius * scaleFactor); s.colour = s.colour.withMultipliedAlpha (alpha); s.offset.x = roundToInt (s.offset.x * scaleFactor); s.offset.y = roundToInt (s.offset.y * scaleFactor); s.drawForImage (g, image); g.setOpacity (alpha); g.drawImageAt (image, 0, 0); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.h000066400000000000000000000075321320201440200324060ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DROPSHADOWEFFECT_H_INCLUDED #define JUCE_DROPSHADOWEFFECT_H_INCLUDED //============================================================================== /** Defines a drop-shadow effect. */ struct JUCE_API DropShadow { /** Creates a default drop-shadow effect. */ DropShadow() noexcept; /** Creates a drop-shadow object with the given parameters. */ DropShadow (Colour shadowColour, int radius, Point offset) noexcept; /** Renders a drop-shadow based on the alpha-channel of the given image. */ void drawForImage (Graphics& g, const Image& srcImage) const; /** Renders a drop-shadow based on the shape of a path. */ void drawForPath (Graphics& g, const Path& path) const; /** Renders a drop-shadow for a rectangle. Note that for speed, this approximates the shadow using gradients. */ void drawForRectangle (Graphics& g, const Rectangle& area) const; /** The colour with which to render the shadow. In most cases you'll probably want to leave this as black with an alpha value of around 0.5 */ Colour colour; /** The approximate spread of the shadow. */ int radius; /** The offset of the shadow. */ Point offset; }; //============================================================================== /** An effect filter that adds a drop-shadow behind the image's content. (This will only work on images/components that aren't opaque, of course). When added to a component, this effect will draw a soft-edged shadow based on what gets drawn inside it. The shadow will also be applied to the component's children. For speed, this doesn't use a proper gaussian blur, but cheats by using a simple bilinear filter. If you need a really high-quality shadow, check out ImageConvolutionKernel::createGaussianBlur() @see Component::setComponentEffect */ class JUCE_API DropShadowEffect : public ImageEffectFilter { public: //============================================================================== /** Creates a default drop-shadow effect. To customise the shadow's appearance, use the setShadowProperties() method. */ DropShadowEffect(); /** Destructor. */ ~DropShadowEffect(); //============================================================================== /** Sets up parameters affecting the shadow's appearance. */ void setShadowProperties (const DropShadow& newShadow); //============================================================================== /** @internal */ void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha); private: //============================================================================== DropShadow shadow; JUCE_LEAK_DETECTOR (DropShadowEffect) }; #endif // JUCE_DROPSHADOWEFFECT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.cpp000066400000000000000000000034141320201440200315720ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ GlowEffect::GlowEffect() : radius (2.0f), colour (Colours::white) { } GlowEffect::~GlowEffect() { } void GlowEffect::setGlowProperties (const float newRadius, Colour newColour) { radius = newRadius; colour = newColour; } void GlowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) { Image temp (image.getFormat(), image.getWidth(), image.getHeight(), true); ImageConvolutionKernel blurKernel (roundToInt (radius * scaleFactor * 2.0f)); blurKernel.createGaussianBlur (radius); blurKernel.rescaleAllValues (radius); blurKernel.applyToImage (temp, image, image.getBounds()); g.setColour (colour.withMultipliedAlpha (alpha)); g.drawImageAt (temp, 0, 0, true); g.setOpacity (alpha); g.drawImageAt (image, 0, 0, false); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.h000066400000000000000000000046521320201440200312440ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_GLOWEFFECT_H_INCLUDED #define JUCE_GLOWEFFECT_H_INCLUDED //============================================================================== /** A component effect that adds a coloured blur around the component's contents. (This will only work on non-opaque components). @see Component::setComponentEffect, DropShadowEffect */ class JUCE_API GlowEffect : public ImageEffectFilter { public: //============================================================================== /** Creates a default 'glow' effect. To customise its appearance, use the setGlowProperties() method. */ GlowEffect(); /** Destructor. */ ~GlowEffect(); //============================================================================== /** Sets the glow's radius and colour. The radius is how large the blur should be, and the colour is used to render it (for a less intense glow, lower the colour's opacity). */ void setGlowProperties (float newRadius, Colour newColour); //============================================================================== /** @internal */ void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha); private: //============================================================================== float radius; Colour colour; JUCE_LEAK_DETECTOR (GlowEffect) }; #endif // JUCE_GLOWEFFECT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/effects/juce_ImageEffectFilter.h000066400000000000000000000053751320201440200325270ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IMAGEEFFECTFILTER_H_INCLUDED #define JUCE_IMAGEEFFECTFILTER_H_INCLUDED //============================================================================== /** A graphical effect filter that can be applied to components. An ImageEffectFilter can be applied to the image that a component paints before it hits the screen. This is used for adding effects like shadows, blurs, etc. @see Component::setComponentEffect */ class JUCE_API ImageEffectFilter { public: //============================================================================== /** Overridden to render the effect. The implementation of this method must use the image that is passed in as its source, and should render its output to the graphics context passed in. @param sourceImage the image that the source component has just rendered with its paint() method. The image may or may not have an alpha channel, depending on whether the component is opaque. @param destContext the graphics context to use to draw the resultant image. @param scaleFactor a scale factor that has been applied to the image - e.g. if this is 2, then the image is actually scaled-up to twice the original resolution @param alpha the alpha with which to draw the resultant image to the target context */ virtual void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha) = 0; /** Destructor. */ virtual ~ImageEffectFilter() {} }; #endif // JUCE_IMAGEEFFECTFILTER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/000077500000000000000000000000001320201440200255435ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp000066400000000000000000000154701320201440200325620ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AttributedString::Attribute::Attribute (Range range_, Colour colour_) : range (range_), colour (new Colour (colour_)) { } AttributedString::Attribute::Attribute (Range range_, const Font& font_) : range (range_), font (new Font (font_)) { } AttributedString::Attribute::Attribute (const Attribute& other) : range (other.range), font (other.font.createCopy()), colour (other.colour.createCopy()) { } AttributedString::Attribute::Attribute (const Attribute& other, const int offset) : range (other.range + offset), font (other.font.createCopy()), colour (other.colour.createCopy()) { } AttributedString::Attribute::~Attribute() {} //============================================================================== AttributedString::AttributedString() : lineSpacing (0.0f), justification (Justification::left), wordWrap (AttributedString::byWord), readingDirection (AttributedString::natural) { } AttributedString::AttributedString (const String& newString) : text (newString), lineSpacing (0.0f), justification (Justification::left), wordWrap (AttributedString::byWord), readingDirection (AttributedString::natural) { } AttributedString::AttributedString (const AttributedString& other) : text (other.text), lineSpacing (other.lineSpacing), justification (other.justification), wordWrap (other.wordWrap), readingDirection (other.readingDirection) { attributes.addCopiesOf (other.attributes); } AttributedString& AttributedString::operator= (const AttributedString& other) { if (this != &other) { text = other.text; lineSpacing = other.lineSpacing; justification = other.justification; wordWrap = other.wordWrap; readingDirection = other.readingDirection; attributes.clear(); attributes.addCopiesOf (other.attributes); } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS AttributedString::AttributedString (AttributedString&& other) noexcept : text (static_cast (other.text)), lineSpacing (other.lineSpacing), justification (other.justification), wordWrap (other.wordWrap), readingDirection (other.readingDirection), attributes (static_cast &&> (other.attributes)) { } AttributedString& AttributedString::operator= (AttributedString&& other) noexcept { text = static_cast (other.text); lineSpacing = other.lineSpacing; justification = other.justification; wordWrap = other.wordWrap; readingDirection = other.readingDirection; attributes = static_cast &&> (other.attributes); return *this; } #endif AttributedString::~AttributedString() {} void AttributedString::setText (const String& other) { text = other; } void AttributedString::append (const String& textToAppend) { text += textToAppend; } void AttributedString::append (const String& textToAppend, const Font& font) { const int oldLength = text.length(); const int newLength = textToAppend.length(); text += textToAppend; setFont (Range (oldLength, oldLength + newLength), font); } void AttributedString::append (const String& textToAppend, Colour colour) { const int oldLength = text.length(); const int newLength = textToAppend.length(); text += textToAppend; setColour (Range (oldLength, oldLength + newLength), colour); } void AttributedString::append (const String& textToAppend, const Font& font, Colour colour) { const int oldLength = text.length(); const int newLength = textToAppend.length(); text += textToAppend; setFont (Range (oldLength, oldLength + newLength), font); setColour (Range (oldLength, oldLength + newLength), colour); } void AttributedString::append (const AttributedString& other) { const int originalLength = text.length(); text += other.text; for (int i = 0; i < other.attributes.size(); ++i) attributes.add (new Attribute (*other.attributes.getUnchecked(i), originalLength)); } void AttributedString::clear() { text.clear(); attributes.clear(); } void AttributedString::setJustification (Justification newJustification) noexcept { justification = newJustification; } void AttributedString::setWordWrap (WordWrap newWordWrap) noexcept { wordWrap = newWordWrap; } void AttributedString::setReadingDirection (ReadingDirection newReadingDirection) noexcept { readingDirection = newReadingDirection; } void AttributedString::setLineSpacing (const float newLineSpacing) noexcept { lineSpacing = newLineSpacing; } void AttributedString::setColour (Range range, Colour colour) { attributes.add (new Attribute (range, colour)); } void AttributedString::setColour (Colour colour) { for (int i = attributes.size(); --i >= 0;) if (attributes.getUnchecked(i)->getColour() != nullptr) attributes.remove (i); setColour (Range (0, text.length()), colour); } void AttributedString::setFont (Range range, const Font& font) { attributes.add (new Attribute (range, font)); } void AttributedString::setFont (const Font& font) { for (int i = attributes.size(); --i >= 0;) if (attributes.getUnchecked(i)->getFont() != nullptr) attributes.remove (i); setFont (Range (0, text.length()), font); } void AttributedString::draw (Graphics& g, const Rectangle& area) const { if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer())) { if (! g.getInternalContext().drawTextLayout (*this, area)) { TextLayout layout; layout.createLayout (*this, area.getWidth()); layout.draw (g, area); } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.h000066400000000000000000000207541320201440200322300ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_ATTRIBUTEDSTRING_H_INCLUDED #define JUCE_ATTRIBUTEDSTRING_H_INCLUDED //============================================================================== /** A text string with a set of colour/font settings that are associated with sub-ranges of the text. An attributed string lets you create a string with varied fonts, colours, word-wrapping, layout, etc., and draw it using AttributedString::draw(). @see TextLayout */ class JUCE_API AttributedString { public: /** Creates an empty attributed string. */ AttributedString(); /** Creates an attributed string with the given text. */ explicit AttributedString (const String& text); AttributedString (const AttributedString&); AttributedString& operator= (const AttributedString&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS AttributedString (AttributedString&&) noexcept; AttributedString& operator= (AttributedString&&) noexcept; #endif /** Destructor. */ ~AttributedString(); //============================================================================== /** Returns the complete text of this attributed string. */ const String& getText() const noexcept { return text; } /** Replaces all the text. This will change the text, but won't affect any of the colour or font attributes that have been added. */ void setText (const String& newText); /** Appends some text (with a default font and colour). */ void append (const String& textToAppend); /** Appends some text, with a specified font, and the default colour (black). */ void append (const String& textToAppend, const Font& font); /** Appends some text, with a specified colour, and the default font. */ void append (const String& textToAppend, Colour colour); /** Appends some text, with a specified font and colour. */ void append (const String& textToAppend, const Font& font, Colour colour); /** Appends another AttributedString to this one. Note that this will only append the text, fonts, and colours - it won't copy any other properties such as justification, line-spacing, etc from the other object. */ void append (const AttributedString& other); /** Resets the string, clearing all text and attributes. Note that this won't affect global settings like the justification type, word-wrap mode, etc. */ void clear(); //============================================================================== /** Draws this string within the given area. The layout of the string within the rectangle is controlled by the justification value passed to setJustification(). */ void draw (Graphics& g, const Rectangle& area) const; //============================================================================== /** Returns the justification that should be used for laying-out the text. This may include both vertical and horizontal flags. */ Justification getJustification() const noexcept { return justification; } /** Sets the justification that should be used for laying-out the text. This may include both vertical and horizontal flags. */ void setJustification (Justification newJustification) noexcept; //============================================================================== /** Types of word-wrap behaviour. @see getWordWrap, setWordWrap */ enum WordWrap { none, /**< No word-wrapping: lines extend indefinitely. */ byWord, /**< Lines are wrapped on a word boundary. */ byChar, /**< Lines are wrapped on a character boundary. */ }; /** Returns the word-wrapping behaviour. */ WordWrap getWordWrap() const noexcept { return wordWrap; } /** Sets the word-wrapping behaviour. */ void setWordWrap (WordWrap newWordWrap) noexcept; //============================================================================== /** Types of reading direction that can be used. @see getReadingDirection, setReadingDirection */ enum ReadingDirection { natural, leftToRight, rightToLeft, }; /** Returns the reading direction for the text. */ ReadingDirection getReadingDirection() const noexcept { return readingDirection; } /** Sets the reading direction that should be used for the text. */ void setReadingDirection (ReadingDirection newReadingDirection) noexcept; //============================================================================== /** Returns the extra line-spacing distance. */ float getLineSpacing() const noexcept { return lineSpacing; } /** Sets an extra line-spacing distance. */ void setLineSpacing (float newLineSpacing) noexcept; //============================================================================== /** An attribute that has been applied to a range of characters in an AttributedString. */ class JUCE_API Attribute { public: /** Creates an attribute that changes the colour for a range of characters. @see AttributedString::setColour() */ Attribute (Range range, Colour colour); /** Creates an attribute that changes the font for a range of characters. @see AttributedString::setFont() */ Attribute (Range range, const Font& font); Attribute (const Attribute&); ~Attribute(); /** If this attribute specifies a font, this returns it; otherwise it returns nullptr. */ const Font* getFont() const noexcept { return font; } /** If this attribute specifies a colour, this returns it; otherwise it returns nullptr. */ const Colour* getColour() const noexcept { return colour; } /** The range of characters to which this attribute will be applied. */ const Range range; private: ScopedPointer font; ScopedPointer colour; friend class AttributedString; Attribute (const Attribute&, int); Attribute& operator= (const Attribute&); JUCE_LEAK_DETECTOR (Attribute) }; /** Returns the number of attributes that have been added to this string. */ int getNumAttributes() const noexcept { return attributes.size(); } /** Returns one of the string's attributes. The index provided must be less than getNumAttributes(), and >= 0. */ const Attribute* getAttribute (int index) const noexcept { return attributes.getUnchecked (index); } //============================================================================== /** Adds a colour attribute for the specified range. */ void setColour (Range range, Colour colour); /** Removes all existing colour attributes, and applies this colour to the whole string. */ void setColour (Colour colour); /** Adds a font attribute for the specified range. */ void setFont (Range range, const Font& font); /** Removes all existing font attributes, and applies this font to the whole string. */ void setFont (const Font& font); private: String text; float lineSpacing; Justification justification; WordWrap wordWrap; ReadingDirection readingDirection; OwnedArray attributes; JUCE_LEAK_DETECTOR (AttributedString) }; #endif // JUCE_ATTRIBUTEDSTRING_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp000066400000000000000000000313261320201440200322150ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class CustomTypeface::GlyphInfo { public: GlyphInfo (const juce_wchar c, const Path& p, const float w) noexcept : character (c), path (p), width (w) { } struct KerningPair { juce_wchar character2; float kerningAmount; }; void addKerningPair (const juce_wchar subsequentCharacter, const float extraKerningAmount) noexcept { KerningPair kp; kp.character2 = subsequentCharacter; kp.kerningAmount = extraKerningAmount; kerningPairs.add (kp); } float getHorizontalSpacing (const juce_wchar subsequentCharacter) const noexcept { if (subsequentCharacter != 0) for (int i = kerningPairs.size(); --i >= 0;) if (kerningPairs.getReference(i).character2 == subsequentCharacter) return width + kerningPairs.getReference(i).kerningAmount; return width; } const juce_wchar character; const Path path; float width; Array kerningPairs; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphInfo) }; //============================================================================== namespace CustomTypefaceHelpers { static juce_wchar readChar (InputStream& in) { uint32 n = (uint32) (uint16) in.readShort(); if (n >= 0xd800 && n <= 0xdfff) { const uint32 nextWord = (uint32) (uint16) in.readShort(); jassert (nextWord >= 0xdc00); // illegal unicode character! n = 0x10000 + (((n - 0xd800) << 10) | (nextWord - 0xdc00)); } return (juce_wchar) n; } static void writeChar (OutputStream& out, juce_wchar charToWrite) { if (charToWrite >= 0x10000) { charToWrite -= 0x10000; out.writeShort ((short) (uint16) (0xd800 + (charToWrite >> 10))); out.writeShort ((short) (uint16) (0xdc00 + (charToWrite & 0x3ff))); } else { out.writeShort ((short) (uint16) charToWrite); } } } //============================================================================== CustomTypeface::CustomTypeface() : Typeface (String(), String()) { clear(); } CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) : Typeface (String(), String()) { clear(); GZIPDecompressorInputStream gzin (serialisedTypefaceStream); BufferedInputStream in (gzin, 32768); name = in.readString(); const bool isBold = in.readBool(); const bool isItalic = in.readBool(); style = FontStyleHelpers::getStyleName (isBold, isItalic); ascent = in.readFloat(); defaultCharacter = CustomTypefaceHelpers::readChar (in); int numChars = in.readInt(); for (int i = 0; i < numChars; ++i) { const juce_wchar c = CustomTypefaceHelpers::readChar (in); const float width = in.readFloat(); Path p; p.loadPathFromStream (in); addGlyph (c, p, width); } const int numKerningPairs = in.readInt(); for (int i = 0; i < numKerningPairs; ++i) { const juce_wchar char1 = CustomTypefaceHelpers::readChar (in); const juce_wchar char2 = CustomTypefaceHelpers::readChar (in); addKerningPair (char1, char2, in.readFloat()); } } CustomTypeface::~CustomTypeface() { } //============================================================================== void CustomTypeface::clear() { defaultCharacter = 0; ascent = 1.0f; style = "Regular"; zeromem (lookupTable, sizeof (lookupTable)); glyphs.clear(); } void CustomTypeface::setCharacteristics (const String& newName, const float newAscent, const bool isBold, const bool isItalic, const juce_wchar newDefaultCharacter) noexcept { name = newName; defaultCharacter = newDefaultCharacter; ascent = newAscent; style = FontStyleHelpers::getStyleName (isBold, isItalic); } void CustomTypeface::setCharacteristics (const String& newName, const String& newStyle, const float newAscent, const juce_wchar newDefaultCharacter) noexcept { name = newName; style = newStyle; defaultCharacter = newDefaultCharacter; ascent = newAscent; } void CustomTypeface::addGlyph (const juce_wchar character, const Path& path, const float width) noexcept { // Check that you're not trying to add the same character twice.. jassert (findGlyph (character, false) == nullptr); if (isPositiveAndBelow ((int) character, (int) numElementsInArray (lookupTable))) lookupTable [character] = (short) glyphs.size(); glyphs.add (new GlyphInfo (character, path, width)); } void CustomTypeface::addKerningPair (const juce_wchar char1, const juce_wchar char2, const float extraAmount) noexcept { if (extraAmount != 0) { if (GlyphInfo* const g = findGlyph (char1, true)) g->addKerningPair (char2, extraAmount); else jassertfalse; // can only add kerning pairs for characters that exist! } } CustomTypeface::GlyphInfo* CustomTypeface::findGlyph (const juce_wchar character, const bool loadIfNeeded) noexcept { if (isPositiveAndBelow ((int) character, (int) numElementsInArray (lookupTable)) && lookupTable [character] > 0) return glyphs [(int) lookupTable [(int) character]]; for (int i = 0; i < glyphs.size(); ++i) { GlyphInfo* const g = glyphs.getUnchecked(i); if (g->character == character) return g; } if (loadIfNeeded && loadGlyphIfPossible (character)) return findGlyph (character, false); return nullptr; } bool CustomTypeface::loadGlyphIfPossible (const juce_wchar /*characterNeeded*/) { return false; } void CustomTypeface::addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept { setCharacteristics (name, style, typefaceToCopy.getAscent(), defaultCharacter); for (int i = 0; i < numCharacters; ++i) { const juce_wchar c = (juce_wchar) (characterStartIndex + i); Array glyphIndexes; Array offsets; typefaceToCopy.getGlyphPositions (String::charToString (c), glyphIndexes, offsets); const int glyphIndex = glyphIndexes.getFirst(); if (glyphIndex >= 0 && glyphIndexes.size() > 0) { const float glyphWidth = offsets[1]; Path p; typefaceToCopy.getOutlineForGlyph (glyphIndex, p); addGlyph (c, p, glyphWidth); for (int j = glyphs.size() - 1; --j >= 0;) { const juce_wchar char2 = glyphs.getUnchecked (j)->character; glyphIndexes.clearQuick(); offsets.clearQuick(); typefaceToCopy.getGlyphPositions (String::charToString (c) + String::charToString (char2), glyphIndexes, offsets); if (offsets.size() > 1) addKerningPair (c, char2, offsets[1] - glyphWidth); } } } } bool CustomTypeface::writeToStream (OutputStream& outputStream) { GZIPCompressorOutputStream out (&outputStream); out.writeString (name); out.writeBool (FontStyleHelpers::isBold (style)); out.writeBool (FontStyleHelpers::isItalic (style)); out.writeFloat (ascent); CustomTypefaceHelpers::writeChar (out, defaultCharacter); out.writeInt (glyphs.size()); int numKerningPairs = 0; for (int i = 0; i < glyphs.size(); ++i) { const GlyphInfo* const g = glyphs.getUnchecked (i); CustomTypefaceHelpers::writeChar (out, g->character); out.writeFloat (g->width); g->path.writePathToStream (out); numKerningPairs += g->kerningPairs.size(); } out.writeInt (numKerningPairs); for (int i = 0; i < glyphs.size(); ++i) { const GlyphInfo* const g = glyphs.getUnchecked (i); for (int j = 0; j < g->kerningPairs.size(); ++j) { const GlyphInfo::KerningPair& p = g->kerningPairs.getReference (j); CustomTypefaceHelpers::writeChar (out, g->character); CustomTypefaceHelpers::writeChar (out, p.character2); out.writeFloat (p.kerningAmount); } } return true; } //============================================================================== float CustomTypeface::getAscent() const { return ascent; } float CustomTypeface::getDescent() const { return 1.0f - ascent; } float CustomTypeface::getHeightToPointsFactor() const { return ascent; } float CustomTypeface::getStringWidth (const String& text) { float x = 0; for (String::CharPointerType t (text.getCharPointer()); ! t.isEmpty();) { const juce_wchar c = t.getAndAdvance(); if (const GlyphInfo* const glyph = findGlyph (c, true)) { x += glyph->getHorizontalSpacing (*t); } else { const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) x += fallbackTypeface->getStringWidth (String::charToString (c)); } } return x; } void CustomTypeface::getGlyphPositions (const String& text, Array & resultGlyphs, Array& xOffsets) { xOffsets.add (0); float x = 0; for (String::CharPointerType t (text.getCharPointer()); ! t.isEmpty();) { float width = 0.0f; int glyphChar = 0; const juce_wchar c = t.getAndAdvance(); if (const GlyphInfo* const glyph = findGlyph (c, true)) { width = glyph->getHorizontalSpacing (*t); glyphChar = (int) glyph->character; } else { const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) { Array subGlyphs; Array subOffsets; fallbackTypeface->getGlyphPositions (String::charToString (c), subGlyphs, subOffsets); if (subGlyphs.size() > 0) { glyphChar = subGlyphs.getFirst(); width = subOffsets[1]; } } } x += width; resultGlyphs.add (glyphChar); xOffsets.add (x); } } bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) { if (const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true)) { path = glyph->path; return true; } const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) return fallbackTypeface->getOutlineForGlyph (glyphNumber, path); return false; } EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) { if (const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true)) { if (! glyph->path.isEmpty()) return new EdgeTable (glyph->path.getBoundsTransformed (transform) .getSmallestIntegerContainer().expanded (1, 0), glyph->path, transform); } else { const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform, fontHeight); } return nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.h000066400000000000000000000170401320201440200316570ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_CUSTOMTYPEFACE_H_INCLUDED #define JUCE_CUSTOMTYPEFACE_H_INCLUDED //============================================================================== /** A typeface that can be populated with custom glyphs. You can create a CustomTypeface if you need one that contains your own glyphs, or if you need to load a typeface from a Juce-formatted binary stream. If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface() to copy glyphs into this face. NOTE! For most people this class is almost certainly NOT the right tool to use! If what you want to do is to embed a font into your exe, then your best plan is probably to embed your TTF/OTF font file into your binary using the Introjucer, and then call Typeface::createSystemTypefaceFor() to load it from memory. @see Typeface, Font */ class JUCE_API CustomTypeface : public Typeface { public: //============================================================================== /** Creates a new, empty typeface. */ CustomTypeface(); /** Loads a typeface from a previously saved stream. The stream must have been created by writeToStream(). NOTE! Since this class was written, support was added for loading real font files from memory, so for most people, using Typeface::createSystemTypefaceFor() to load a real font is more appropriate than using this class to store it in a proprietary format. @see writeToStream */ explicit CustomTypeface (InputStream& serialisedTypefaceStream); /** Destructor. */ ~CustomTypeface(); //============================================================================== /** Resets this typeface, deleting all its glyphs and settings. */ void clear(); /** Sets the vital statistics for the typeface. @param fontFamily the typeface's font family @param ascent the ascent - this is normalised to a height of 1.0 and this is the value that will be returned by Typeface::getAscent(). The descent is assumed to be (1.0 - ascent) @param isBold should be true if the typeface is bold @param isItalic should be true if the typeface is italic @param defaultCharacter the character to be used as a replacement if there's no glyph available for the character that's being drawn */ void setCharacteristics (const String& fontFamily, float ascent, bool isBold, bool isItalic, juce_wchar defaultCharacter) noexcept; /** Sets the vital statistics for the typeface. @param fontFamily the typeface's font family @param fontStyle the typeface's font style @param ascent the ascent - this is normalised to a height of 1.0 and this is the value that will be returned by Typeface::getAscent(). The descent is assumed to be (1.0 - ascent) @param defaultCharacter the character to be used as a replacement if there's no glyph available for the character that's being drawn */ void setCharacteristics (const String& fontFamily, const String& fontStyle, float ascent, juce_wchar defaultCharacter) noexcept; /** Adds a glyph to the typeface. The path that is passed in is normalised so that the font height is 1.0, and its origin is the anchor point of the character on its baseline. The width is the nominal width of the character, and any extra kerning values that are specified will be added to this width. */ void addGlyph (juce_wchar character, const Path& path, float width) noexcept; /** Specifies an extra kerning amount to be used between a pair of characters. The amount will be added to the nominal width of the first character when laying out a string. */ void addKerningPair (juce_wchar char1, juce_wchar char2, float extraAmount) noexcept; /** Adds a range of glyphs from another typeface. This will attempt to pull in the paths and kerning information from another typeface and add it to this one. */ void addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept; /** Saves this typeface as a Juce-formatted font file. A CustomTypeface can be created to reload the data that is written - see the CustomTypeface constructor. NOTE! Since this class was written, support was added for loading real font files from memory, so for most people, using Typeface::createSystemTypefaceFor() to load a real font is more appropriate than using this class to store it in a proprietary format. */ bool writeToStream (OutputStream& outputStream); //============================================================================== // The following methods implement the basic Typeface behaviour. float getAscent() const override; float getDescent() const override; float getHeightToPointsFactor() const override; float getStringWidth (const String&) override; void getGlyphPositions (const String&, Array & glyphs, Array& xOffsets) override; bool getOutlineForGlyph (int glyphNumber, Path&) override; EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&, float fontHeight) override; protected: //============================================================================== juce_wchar defaultCharacter; float ascent; //============================================================================== /** If a subclass overrides this, it can load glyphs into the font on-demand. When methods such as getGlyphPositions() or getOutlineForGlyph() are asked for a particular character and there's no corresponding glyph, they'll call this method so that a subclass can try to add that glyph, returning true if it manages to do so. */ virtual bool loadGlyphIfPossible (juce_wchar characterNeeded); private: //============================================================================== class GlyphInfo; friend struct ContainerDeletePolicy; OwnedArray glyphs; short lookupTable [128]; GlyphInfo* findGlyph (const juce_wchar character, bool loadIfNeeded) noexcept; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTypeface) }; #endif // JUCE_CUSTOMTYPEFACE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp000066400000000000000000000522611320201440200301710ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace FontValues { static float limitFontHeight (const float height) noexcept { return jlimit (0.1f, 10000.0f, height); } const float defaultFontHeight = 14.0f; float minimumHorizontalScale = 0.7f; String fallbackFont; String fallbackFontStyle; } typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); GetTypefaceForFont juce_getTypefaceForFont = nullptr; float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; } void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; } //============================================================================== class TypefaceCache : private DeletedAtShutdown { public: TypefaceCache() : counter (0) { setSize (10); } ~TypefaceCache() { clearSingletonInstance(); } juce_DeclareSingleton (TypefaceCache, false) void setSize (const int numToCache) { const ScopedWriteLock sl (lock); faces.clear(); faces.insertMultiple (-1, CachedFace(), numToCache); } void clear() { const ScopedWriteLock sl (lock); setSize (faces.size()); defaultFace = nullptr; } Typeface::Ptr findTypefaceFor (const Font& font) { const ScopedReadLock slr (lock); const String faceName (font.getTypefaceName()); const String faceStyle (font.getTypefaceStyle()); jassert (faceName.isNotEmpty()); for (int i = faces.size(); --i >= 0;) { CachedFace& face = faces.getReference(i); if (face.typefaceName == faceName && face.typefaceStyle == faceStyle && face.typeface != nullptr && face.typeface->isSuitableForFont (font)) { face.lastUsageCount = ++counter; return face.typeface; } } const ScopedWriteLock slw (lock); int replaceIndex = 0; size_t bestLastUsageCount = std::numeric_limits::max(); for (int i = faces.size(); --i >= 0;) { const size_t lu = faces.getReference(i).lastUsageCount; if (bestLastUsageCount > lu) { bestLastUsageCount = lu; replaceIndex = i; } } CachedFace& face = faces.getReference (replaceIndex); face.typefaceName = faceName; face.typefaceStyle = faceStyle; face.lastUsageCount = ++counter; if (juce_getTypefaceForFont == nullptr) face.typeface = Font::getDefaultTypefaceForFont (font); else face.typeface = juce_getTypefaceForFont (font); jassert (face.typeface != nullptr); // the look and feel must return a typeface! if (defaultFace == nullptr && font == Font()) defaultFace = face.typeface; return face.typeface; } Typeface::Ptr defaultFace; private: struct CachedFace { CachedFace() noexcept : lastUsageCount (0) {} // Although it seems a bit wacky to store the name here, it's because it may be a // placeholder rather than a real one, e.g. "" vs the actual typeface name. // Since the typeface itself doesn't know that it may have this alias, the name under // which it was fetched needs to be stored separately. String typefaceName, typefaceStyle; size_t lastUsageCount; Typeface::Ptr typeface; }; ReadWriteLock lock; Array faces; size_t counter; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache) }; juce_ImplementSingleton (TypefaceCache) void Typeface::setTypefaceCacheSize (int numFontsToCache) { TypefaceCache::getInstance()->setSize (numFontsToCache); } #if JUCE_MODULE_AVAILABLE_juce_opengl extern void clearOpenGLGlyphCache(); #endif void Typeface::clearTypefaceCache() { TypefaceCache::getInstance()->clear(); RenderingHelpers::SoftwareRendererSavedState::clearGlyphCache(); #if JUCE_MODULE_AVAILABLE_juce_opengl clearOpenGLGlyphCache(); #endif } //============================================================================== class Font::SharedFontInternal : public ReferenceCountedObject { public: SharedFontInternal() noexcept : typeface (TypefaceCache::getInstance()->defaultFace), typefaceName (Font::getDefaultSansSerifFontName()), typefaceStyle (Font::getDefaultStyle()), height (FontValues::defaultFontHeight), horizontalScale (1.0f), kerning (0), ascent (0), underline (false) { } SharedFontInternal (int styleFlags, float fontHeight) noexcept : typefaceName (Font::getDefaultSansSerifFontName()), typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)), height (fontHeight), horizontalScale (1.0f), kerning (0), ascent (0), underline ((styleFlags & underlined) != 0) { if (styleFlags == plain) typeface = TypefaceCache::getInstance()->defaultFace; } SharedFontInternal (const String& name, int styleFlags, float fontHeight) noexcept : typefaceName (name), typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)), height (fontHeight), horizontalScale (1.0f), kerning (0), ascent (0), underline ((styleFlags & underlined) != 0) { if (styleFlags == plain && typefaceName.isEmpty()) typeface = TypefaceCache::getInstance()->defaultFace; } SharedFontInternal (const String& name, const String& style, float fontHeight) noexcept : typefaceName (name), typefaceStyle (style), height (fontHeight), horizontalScale (1.0f), kerning (0), ascent (0), underline (false) { if (typefaceName.isEmpty()) typefaceName = Font::getDefaultSansSerifFontName(); } explicit SharedFontInternal (const Typeface::Ptr& face) noexcept : typeface (face), typefaceName (face->getName()), typefaceStyle (face->getStyle()), height (FontValues::defaultFontHeight), horizontalScale (1.0f), kerning (0), ascent (0), underline (false) { jassert (typefaceName.isNotEmpty()); } SharedFontInternal (const SharedFontInternal& other) noexcept : ReferenceCountedObject(), typeface (other.typeface), typefaceName (other.typefaceName), typefaceStyle (other.typefaceStyle), height (other.height), horizontalScale (other.horizontalScale), kerning (other.kerning), ascent (other.ascent), underline (other.underline) { } bool operator== (const SharedFontInternal& other) const noexcept { return height == other.height && underline == other.underline && horizontalScale == other.horizontalScale && kerning == other.kerning && typefaceName == other.typefaceName && typefaceStyle == other.typefaceStyle; } Typeface::Ptr typeface; String typefaceName, typefaceStyle; float height, horizontalScale, kerning, ascent; bool underline; }; //============================================================================== Font::Font() : font (new SharedFontInternal()) {} Font::Font (const Typeface::Ptr& typeface) : font (new SharedFontInternal (typeface)) {} Font::Font (const Font& other) noexcept : font (other.font) {} Font::Font (float fontHeight, int styleFlags) : font (new SharedFontInternal (styleFlags, FontValues::limitFontHeight (fontHeight))) { } Font::Font (const String& typefaceName, float fontHeight, int styleFlags) : font (new SharedFontInternal (typefaceName, styleFlags, FontValues::limitFontHeight (fontHeight))) { } Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight) : font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight))) { } Font& Font::operator= (const Font& other) noexcept { font = other.font; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Font::Font (Font&& other) noexcept : font (static_cast&&> (other.font)) { } Font& Font::operator= (Font&& other) noexcept { font = static_cast&&> (other.font); return *this; } #endif Font::~Font() noexcept { } bool Font::operator== (const Font& other) const noexcept { return font == other.font || *font == *other.font; } bool Font::operator!= (const Font& other) const noexcept { return ! operator== (other); } void Font::dupeInternalIfShared() { if (font->getReferenceCount() > 1) font = new SharedFontInternal (*font); } void Font::checkTypefaceSuitability() { if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this)) font->typeface = nullptr; } //============================================================================== struct FontPlaceholderNames { FontPlaceholderNames() : sans (""), serif (""), mono (""), regular ("") { } String sans, serif, mono, regular; }; const FontPlaceholderNames& getFontPlaceholderNames() { static FontPlaceholderNames names; return names; } #if JUCE_MSVC // This is a workaround for the lack of thread-safety in MSVC's handling of function-local // statics - if multiple threads all try to create the first Font object at the same time, // it can cause a race-condition in creating these placeholder strings. struct FontNamePreloader { FontNamePreloader() { getFontPlaceholderNames(); } }; static FontNamePreloader fnp; #endif const String& Font::getDefaultSansSerifFontName() { return getFontPlaceholderNames().sans; } const String& Font::getDefaultSerifFontName() { return getFontPlaceholderNames().serif; } const String& Font::getDefaultMonospacedFontName() { return getFontPlaceholderNames().mono; } const String& Font::getDefaultStyle() { return getFontPlaceholderNames().regular; } const String& Font::getTypefaceName() const noexcept { return font->typefaceName; } const String& Font::getTypefaceStyle() const noexcept { return font->typefaceStyle; } void Font::setTypefaceName (const String& faceName) { if (faceName != font->typefaceName) { jassert (faceName.isNotEmpty()); dupeInternalIfShared(); font->typefaceName = faceName; font->typeface = nullptr; font->ascent = 0; } } void Font::setTypefaceStyle (const String& typefaceStyle) { if (typefaceStyle != font->typefaceStyle) { dupeInternalIfShared(); font->typefaceStyle = typefaceStyle; font->typeface = nullptr; font->ascent = 0; } } Font Font::withTypefaceStyle (const String& newStyle) const { Font f (*this); f.setTypefaceStyle (newStyle); return f; } StringArray Font::getAvailableStyles() const { return findAllTypefaceStyles (getTypeface()->getName()); } Typeface* Font::getTypeface() const { if (font->typeface == nullptr) { font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this); jassert (font->typeface != nullptr); } return font->typeface; } //============================================================================== const String& Font::getFallbackFontName() { return FontValues::fallbackFont; } void Font::setFallbackFontName (const String& name) { FontValues::fallbackFont = name; #if JUCE_MAC || JUCE_IOS jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX.. #endif } const String& Font::getFallbackFontStyle() { return FontValues::fallbackFontStyle; } void Font::setFallbackFontStyle (const String& style) { FontValues::fallbackFontStyle = style; #if JUCE_MAC || JUCE_IOS jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX.. #endif } //============================================================================== Font Font::withHeight (const float newHeight) const { Font f (*this); f.setHeight (newHeight); return f; } float Font::getHeightToPointsFactor() const { return getTypeface()->getHeightToPointsFactor(); } Font Font::withPointHeight (float heightInPoints) const { Font f (*this); f.setHeight (heightInPoints / getHeightToPointsFactor()); return f; } void Font::setHeight (float newHeight) { newHeight = FontValues::limitFontHeight (newHeight); if (font->height != newHeight) { dupeInternalIfShared(); font->height = newHeight; checkTypefaceSuitability(); } } void Font::setHeightWithoutChangingWidth (float newHeight) { newHeight = FontValues::limitFontHeight (newHeight); if (font->height != newHeight) { dupeInternalIfShared(); font->horizontalScale *= (font->height / newHeight); font->height = newHeight; checkTypefaceSuitability(); } } int Font::getStyleFlags() const noexcept { int styleFlags = font->underline ? underlined : plain; if (isBold()) styleFlags |= bold; if (isItalic()) styleFlags |= italic; return styleFlags; } Font Font::withStyle (const int newFlags) const { Font f (*this); f.setStyleFlags (newFlags); return f; } void Font::setStyleFlags (const int newFlags) { if (getStyleFlags() != newFlags) { dupeInternalIfShared(); font->typeface = nullptr; font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags); font->underline = (newFlags & underlined) != 0; font->ascent = 0; } } void Font::setSizeAndStyle (float newHeight, const int newStyleFlags, const float newHorizontalScale, const float newKerningAmount) { newHeight = FontValues::limitFontHeight (newHeight); if (font->height != newHeight || font->horizontalScale != newHorizontalScale || font->kerning != newKerningAmount) { dupeInternalIfShared(); font->height = newHeight; font->horizontalScale = newHorizontalScale; font->kerning = newKerningAmount; checkTypefaceSuitability(); } setStyleFlags (newStyleFlags); } void Font::setSizeAndStyle (float newHeight, const String& newStyle, const float newHorizontalScale, const float newKerningAmount) { newHeight = FontValues::limitFontHeight (newHeight); if (font->height != newHeight || font->horizontalScale != newHorizontalScale || font->kerning != newKerningAmount) { dupeInternalIfShared(); font->height = newHeight; font->horizontalScale = newHorizontalScale; font->kerning = newKerningAmount; checkTypefaceSuitability(); } setTypefaceStyle (newStyle); } Font Font::withHorizontalScale (const float newHorizontalScale) const { Font f (*this); f.setHorizontalScale (newHorizontalScale); return f; } void Font::setHorizontalScale (const float scaleFactor) { dupeInternalIfShared(); font->horizontalScale = scaleFactor; checkTypefaceSuitability(); } float Font::getHorizontalScale() const noexcept { return font->horizontalScale; } float Font::getExtraKerningFactor() const noexcept { return font->kerning; } Font Font::withExtraKerningFactor (const float extraKerning) const { Font f (*this); f.setExtraKerningFactor (extraKerning); return f; } void Font::setExtraKerningFactor (const float extraKerning) { dupeInternalIfShared(); font->kerning = extraKerning; checkTypefaceSuitability(); } Font Font::boldened() const { return withStyle (getStyleFlags() | bold); } Font Font::italicised() const { return withStyle (getStyleFlags() | italic); } bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->typefaceStyle); } bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->typefaceStyle); } bool Font::isUnderlined() const noexcept { return font->underline; } void Font::setBold (const bool shouldBeBold) { const int flags = getStyleFlags(); setStyleFlags (shouldBeBold ? (flags | bold) : (flags & ~bold)); } void Font::setItalic (const bool shouldBeItalic) { const int flags = getStyleFlags(); setStyleFlags (shouldBeItalic ? (flags | italic) : (flags & ~italic)); } void Font::setUnderline (const bool shouldBeUnderlined) { dupeInternalIfShared(); font->underline = shouldBeUnderlined; checkTypefaceSuitability(); } float Font::getAscent() const { if (font->ascent == 0) font->ascent = getTypeface()->getAscent(); return font->height * font->ascent; } float Font::getHeight() const noexcept { return font->height; } float Font::getDescent() const { return font->height - getAscent(); } float Font::getHeightInPoints() const { return getHeight() * getHeightToPointsFactor(); } float Font::getAscentInPoints() const { return getAscent() * getHeightToPointsFactor(); } float Font::getDescentInPoints() const { return getDescent() * getHeightToPointsFactor(); } int Font::getStringWidth (const String& text) const { return roundToInt (getStringWidthFloat (text)); } float Font::getStringWidthFloat (const String& text) const { float w = getTypeface()->getStringWidth (text); if (font->kerning != 0) w += font->kerning * text.length(); return w * font->height * font->horizontalScale; } void Font::getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) const { getTypeface()->getGlyphPositions (text, glyphs, xOffsets); const int num = xOffsets.size(); if (num > 0) { const float scale = font->height * font->horizontalScale; float* const x = xOffsets.getRawDataPointer(); if (font->kerning != 0) { for (int i = 0; i < num; ++i) x[i] = (x[i] + i * font->kerning) * scale; } else { for (int i = 0; i < num; ++i) x[i] *= scale; } } } void Font::findFonts (Array& destArray) { const StringArray names (findAllTypefaceNames()); for (int i = 0; i < names.size(); ++i) { const StringArray styles (findAllTypefaceStyles (names[i])); String style ("Regular"); if (! styles.contains (style, true)) style = styles[0]; destArray.add (Font (names[i], style, FontValues::defaultFontHeight)); } } //============================================================================== String Font::toString() const { String s; if (getTypefaceName() != getDefaultSansSerifFontName()) s << getTypefaceName() << "; "; s << String (getHeight(), 1); if (getTypefaceStyle() != getDefaultStyle()) s << ' ' << getTypefaceStyle(); return s; } Font Font::fromString (const String& fontDescription) { const int separator = fontDescription.indexOfChar (';'); String name; if (separator > 0) name = fontDescription.substring (0, separator).trim(); if (name.isEmpty()) name = getDefaultSansSerifFontName(); String sizeAndStyle (fontDescription.substring (separator + 1).trimStart()); float height = sizeAndStyle.getFloatValue(); if (height <= 0) height = 10.0f; const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false)); return Font (name, style, height); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h000066400000000000000000000465611320201440200276440ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_FONT_H_INCLUDED #define JUCE_FONT_H_INCLUDED //============================================================================== /** Represents a particular font, including its size, style, etc. Apart from the typeface to be used, a Font object also dictates whether the font is bold, italic, underlined, how big it is, and its kerning and horizontal scale factor. @see Typeface */ class JUCE_API Font { public: //============================================================================== /** A combination of these values is used by the constructor to specify the style of font to use. */ enum FontStyleFlags { plain = 0, /**< indicates a plain, non-bold, non-italic version of the font. @see setStyleFlags */ bold = 1, /**< boldens the font. @see setStyleFlags */ italic = 2, /**< finds an italic version of the font. @see setStyleFlags */ underlined = 4 /**< underlines the font. @see setStyleFlags */ }; //============================================================================== /** Creates a sans-serif font in a given size. @param fontHeight the height in pixels (can be fractional) @param styleFlags the style to use - this can be a combination of the Font::bold, Font::italic and Font::underlined, or just Font::plain for the normal style. @see FontStyleFlags, getDefaultSansSerifFontName */ Font (float fontHeight, int styleFlags = plain); /** Creates a font with a given typeface and parameters. @param typefaceName the font family of the typeface to use @param fontHeight the height in pixels (can be fractional) @param styleFlags the style to use - this can be a combination of the Font::bold, Font::italic and Font::underlined, or just Font::plain for the normal style. @see FontStyleFlags, getDefaultSansSerifFontName */ Font (const String& typefaceName, float fontHeight, int styleFlags); /** Creates a font with a given typeface and parameters. @param typefaceName the font family of the typeface to use @param typefaceStyle the font style of the typeface to use @param fontHeight the height in pixels (can be fractional) */ Font (const String& typefaceName, const String& typefaceStyle, float fontHeight); /** Creates a copy of another Font object. */ Font (const Font& other) noexcept; /** Creates a font for a typeface. */ Font (const Typeface::Ptr& typeface); /** Creates a basic sans-serif font at a default height. You should use one of the other constructors for creating a font that you're planning on drawing with - this constructor is here to help initialise objects before changing the font's settings later. */ Font(); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Font (Font&& other) noexcept; Font& operator= (Font&& other) noexcept; #endif /** Copies this font from another one. */ Font& operator= (const Font& other) noexcept; bool operator== (const Font& other) const noexcept; bool operator!= (const Font& other) const noexcept; /** Destructor. */ ~Font() noexcept; //============================================================================== /** Changes the font family of the typeface. e.g. "Arial", "Courier", etc. This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font family names, but are generic font family names that are used to represent the various default fonts. If you need to know the exact typeface font family being used, you can call Font::getTypeface()->getName(), which will give you the platform-specific font family. If a suitable font isn't found on the machine, it'll just use a default instead. */ void setTypefaceName (const String& faceName); /** Returns the font family of the typeface that this font uses. e.g. "Arial", "Courier", etc. This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font family names, but are generic font familiy names that are used to represent the various default fonts. If you need to know the exact typeface font family being used, you can call Font::getTypeface()->getName(), which will give you the platform-specific font family. */ const String& getTypefaceName() const noexcept; //============================================================================== /** Returns the font style of the typeface that this font uses. @see withTypefaceStyle, getAvailableStyles() */ const String& getTypefaceStyle() const noexcept; /** Changes the font style of the typeface. @see getAvailableStyles() */ void setTypefaceStyle (const String& newStyle); /** Returns a copy of this font with a new typeface style. @see getAvailableStyles() */ Font withTypefaceStyle (const String& newStyle) const; /** Returns a list of the styles that this font can use. */ StringArray getAvailableStyles() const; //============================================================================== /** Returns a typeface font family that represents the default sans-serif font. This is also the typeface that will be used when a font is created without specifying any typeface details. Note that this method just returns a generic placeholder string that means "the default sans-serif font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName */ static const String& getDefaultSansSerifFontName(); /** Returns a typeface font family that represents the default serif font. Note that this method just returns a generic placeholder string that means "the default serif font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName */ static const String& getDefaultSerifFontName(); /** Returns a typeface font family that represents the default monospaced font. Note that this method just returns a generic placeholder string that means "the default monospaced font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName */ static const String& getDefaultMonospacedFontName(); /** Returns a font style name that represents the default style. Note that this method just returns a generic placeholder string that means "the default font style" - it's not the actual name of the font style of any particular font. @see setTypefaceStyle */ static const String& getDefaultStyle(); /** Returns the default system typeface for the given font. */ static Typeface::Ptr getDefaultTypefaceForFont (const Font& font); //============================================================================== /** Returns a copy of this font with a new height. */ Font withHeight (float height) const; /** Returns a copy of this font with a new height, specified in points. */ Font withPointHeight (float heightInPoints) const; /** Changes the font's height. @see getHeight, withHeight, setHeightWithoutChangingWidth */ void setHeight (float newHeight); /** Changes the font's height without changing its width. This alters the horizontal scale to compensate for the change in height. */ void setHeightWithoutChangingWidth (float newHeight); /** Returns the total height of this font, in pixels. This is the maximum height, from the top of the ascent to the bottom of the descenders. @see withHeight, setHeightWithoutChangingWidth, getAscent */ float getHeight() const noexcept; /** Returns the total height of this font, in points. This is the maximum height, from the top of the ascent to the bottom of the descenders. @see withPointHeight, getHeight */ float getHeightInPoints() const; /** Returns the height of the font above its baseline, in pixels. This is the maximum height from the baseline to the top. @see getHeight, getDescent */ float getAscent() const; /** Returns the height of the font above its baseline, in points. This is the maximum height from the baseline to the top. @see getHeight, getDescent */ float getAscentInPoints() const; /** Returns the amount that the font descends below its baseline, in pixels. This is calculated as (getHeight() - getAscent()). @see getAscent, getHeight */ float getDescent() const; /** Returns the amount that the font descends below its baseline, in points. This is calculated as (getHeight() - getAscent()). @see getAscent, getHeight */ float getDescentInPoints() const; //============================================================================== /** Returns the font's style flags. This will return a bitwise-or'ed combination of values from the FontStyleFlags enum, to describe whether the font is bold, italic, etc. @see FontStyleFlags, withStyle */ int getStyleFlags() const noexcept; /** Returns a copy of this font with the given set of style flags. @param styleFlags a bitwise-or'ed combination of values from the FontStyleFlags enum. @see FontStyleFlags, getStyleFlags */ Font withStyle (int styleFlags) const; /** Changes the font's style. @param newFlags a bitwise-or'ed combination of values from the FontStyleFlags enum. @see FontStyleFlags, withStyle */ void setStyleFlags (int newFlags); //============================================================================== /** Makes the font bold or non-bold. */ void setBold (bool shouldBeBold); /** Returns a copy of this font with the bold attribute set. If the font does not have a bold version, this will return the default font. */ Font boldened() const; /** Returns true if the font is bold. */ bool isBold() const noexcept; /** Makes the font italic or non-italic. */ void setItalic (bool shouldBeItalic); /** Returns a copy of this font with the italic attribute set. */ Font italicised() const; /** Returns true if the font is italic. */ bool isItalic() const noexcept; /** Makes the font underlined or non-underlined. */ void setUnderline (bool shouldBeUnderlined); /** Returns true if the font is underlined. */ bool isUnderlined() const noexcept; //============================================================================== /** Returns the font's horizontal scale. A value of 1.0 is the normal scale, less than this will be narrower, greater than 1.0 will be stretched out. @see withHorizontalScale */ float getHorizontalScale() const noexcept; /** Returns a copy of this font with a new horizontal scale. @param scaleFactor a value of 1.0 is the normal scale, less than this will be narrower, greater than 1.0 will be stretched out. @see getHorizontalScale */ Font withHorizontalScale (float scaleFactor) const; /** Changes the font's horizontal scale factor. @param scaleFactor a value of 1.0 is the normal scale, less than this will be narrower, greater than 1.0 will be stretched out. */ void setHorizontalScale (float scaleFactor); /** Returns the minimum horizontal scale to which fonts may be squashed when trying to create a layout. @see setDefaultMinimumHorizontalScaleFactor */ static float getDefaultMinimumHorizontalScaleFactor() noexcept; /** Sets the minimum horizontal scale to which fonts may be squashed when trying to create a text layout. @see getDefaultMinimumHorizontalScaleFactor */ static void setDefaultMinimumHorizontalScaleFactor (float newMinimumScaleFactor) noexcept; /** Returns the font's kerning. This is the extra space added between adjacent characters, as a proportion of the font's height. A value of zero is normal spacing, positive values will spread the letters out more, and negative values make them closer together. */ float getExtraKerningFactor() const noexcept; /** Returns a copy of this font with a new kerning factor. @param extraKerning a multiple of the font's height that will be added to space between the characters. So a value of zero is normal spacing, positive values spread the letters out, negative values make them closer together. */ Font withExtraKerningFactor (float extraKerning) const; /** Changes the font's kerning. @param extraKerning a multiple of the font's height that will be added to space between the characters. So a value of zero is normal spacing, positive values spread the letters out, negative values make them closer together. */ void setExtraKerningFactor (float extraKerning); //============================================================================== /** Changes all the font's characteristics with one call. */ void setSizeAndStyle (float newHeight, int newStyleFlags, float newHorizontalScale, float newKerningAmount); /** Changes all the font's characteristics with one call. */ void setSizeAndStyle (float newHeight, const String& newStyle, float newHorizontalScale, float newKerningAmount); //============================================================================== /** Returns the total width of a string as it would be drawn using this font. For a more accurate floating-point result, use getStringWidthFloat(). */ int getStringWidth (const String& text) const; /** Returns the total width of a string as it would be drawn using this font. @see getStringWidth */ float getStringWidthFloat (const String& text) const; /** Returns the series of glyph numbers and their x offsets needed to represent a string. An extra x offset is added at the end of the run, to indicate where the right hand edge of the last character is. */ void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) const; //============================================================================== /** Returns the typeface used by this font. Note that the object returned may go out of scope if this font is deleted or has its style changed. */ Typeface* getTypeface() const; /** Creates an array of Font objects to represent all the fonts on the system. If you just need the font family names of the typefaces, you can also use findAllTypefaceNames() instead. @param results the array to which new Font objects will be added. */ static void findFonts (Array& results); /** Returns a list of all the available typeface font families. The names returned can be passed into setTypefaceName(). You can use this instead of findFonts() if you only need their font family names, and not font objects. */ static StringArray findAllTypefaceNames(); /** Returns a list of all the available typeface font styles. The names returned can be passed into setTypefaceStyle(). You can use this instead of findFonts() if you only need their styles, and not font objects. */ static StringArray findAllTypefaceStyles (const String& family); //============================================================================== /** Returns the font family of the typeface to be used for rendering glyphs that aren't found in the requested typeface. */ static const String& getFallbackFontName(); /** Sets the (platform-specific) font family of the typeface to use to find glyphs that aren't available in whatever font you're trying to use. */ static void setFallbackFontName (const String& name); /** Returns the font style of the typeface to be used for rendering glyphs that aren't found in the requested typeface. */ static const String& getFallbackFontStyle(); /** Sets the (platform-specific) font style of the typeface to use to find glyphs that aren't available in whatever font you're trying to use. */ static void setFallbackFontStyle (const String& style); //============================================================================== /** Creates a string to describe this font. The string will contain information to describe the font's typeface, size, and style. To recreate the font from this string, use fromString(). */ String toString() const; /** Recreates a font from its stringified encoding. This method takes a string that was created by toString(), and recreates the original font. */ static Font fromString (const String& fontDescription); private: //============================================================================== class SharedFontInternal; ReferenceCountedObjectPtr font; void dupeInternalIfShared(); void checkTypefaceSuitability(); float getHeightToPointsFactor() const; JUCE_LEAK_DETECTOR (Font) }; #endif // JUCE_FONT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp000066400000000000000000000655111320201440200325340ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ PositionedGlyph::PositionedGlyph() noexcept : character (0), glyph (0), x (0), y (0), w (0), whitespace (false) { } PositionedGlyph::PositionedGlyph (const Font& font_, const juce_wchar character_, const int glyph_, const float x_, const float y_, const float w_, const bool whitespace_) : font (font_), character (character_), glyph (glyph_), x (x_), y (y_), w (w_), whitespace (whitespace_) { } PositionedGlyph::PositionedGlyph (const PositionedGlyph& other) : font (other.font), character (other.character), glyph (other.glyph), x (other.x), y (other.y), w (other.w), whitespace (other.whitespace) { } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS PositionedGlyph::PositionedGlyph (PositionedGlyph&& other) noexcept : font (static_cast (other.font)), character (other.character), glyph (other.glyph), x (other.x), y (other.y), w (other.w), whitespace (other.whitespace) { } PositionedGlyph& PositionedGlyph::operator= (PositionedGlyph&& other) noexcept { font = static_cast (other.font); character = other.character; glyph = other.glyph; x = other.x; y = other.y; w = other.w; whitespace = other.whitespace; return *this; } #endif PositionedGlyph::~PositionedGlyph() {} PositionedGlyph& PositionedGlyph::operator= (const PositionedGlyph& other) { font = other.font; character = other.character; glyph = other.glyph; x = other.x; y = other.y; w = other.w; whitespace = other.whitespace; return *this; } static inline void drawGlyphWithFont (Graphics& g, int glyph, const Font& font, const AffineTransform& t) { LowLevelGraphicsContext& context = g.getInternalContext(); context.setFont (font); context.drawGlyph (glyph, t); } void PositionedGlyph::draw (Graphics& g) const { if (! isWhitespace()) drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y)); } void PositionedGlyph::draw (Graphics& g, const AffineTransform& transform) const { if (! isWhitespace()) drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y).followedBy (transform)); } void PositionedGlyph::createPath (Path& path) const { if (! isWhitespace()) { if (Typeface* const t = font.getTypeface()) { Path p; t->getOutlineForGlyph (glyph, p); path.addPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()) .translated (x, y)); } } } bool PositionedGlyph::hitTest (float px, float py) const { if (getBounds().contains (px, py) && ! isWhitespace()) { if (Typeface* const t = font.getTypeface()) { Path p; t->getOutlineForGlyph (glyph, p); AffineTransform::translation (-x, -y) .scaled (1.0f / (font.getHeight() * font.getHorizontalScale()), 1.0f / font.getHeight()) .transformPoint (px, py); return p.contains (px, py); } } return false; } void PositionedGlyph::moveBy (const float deltaX, const float deltaY) { x += deltaX; y += deltaY; } //============================================================================== GlyphArrangement::GlyphArrangement() { glyphs.ensureStorageAllocated (128); } GlyphArrangement::GlyphArrangement (const GlyphArrangement& other) : glyphs (other.glyphs) { } GlyphArrangement& GlyphArrangement::operator= (const GlyphArrangement& other) { glyphs = other.glyphs; return *this; } GlyphArrangement::~GlyphArrangement() { } //============================================================================== void GlyphArrangement::clear() { glyphs.clear(); } PositionedGlyph& GlyphArrangement::getGlyph (const int index) const noexcept { return glyphs.getReference (index); } //============================================================================== void GlyphArrangement::addGlyphArrangement (const GlyphArrangement& other) { glyphs.addArray (other.glyphs); } void GlyphArrangement::addGlyph (const PositionedGlyph& glyph) { glyphs.add (glyph); } void GlyphArrangement::removeRangeOfGlyphs (int startIndex, const int num) { glyphs.removeRange (startIndex, num < 0 ? glyphs.size() : num); } //============================================================================== void GlyphArrangement::addLineOfText (const Font& font, const String& text, const float xOffset, const float yOffset) { addCurtailedLineOfText (font, text, xOffset, yOffset, 1.0e10f, false); } void GlyphArrangement::addCurtailedLineOfText (const Font& font, const String& text, const float xOffset, const float yOffset, const float maxWidthPixels, const bool useEllipsis) { if (text.isNotEmpty()) { Array newGlyphs; Array xOffsets; font.getGlyphPositions (text, newGlyphs, xOffsets); const int textLen = newGlyphs.size(); glyphs.ensureStorageAllocated (glyphs.size() + textLen); String::CharPointerType t (text.getCharPointer()); for (int i = 0; i < textLen; ++i) { const float nextX = xOffsets.getUnchecked (i + 1); if (nextX > maxWidthPixels + 1.0f) { // curtail the string if it's too wide.. if (useEllipsis && textLen > 3 && glyphs.size() >= 3) insertEllipsis (font, xOffset + maxWidthPixels, 0, glyphs.size()); break; } const float thisX = xOffsets.getUnchecked (i); const bool isWhitespace = t.isWhitespace(); glyphs.add (PositionedGlyph (font, t.getAndAdvance(), newGlyphs.getUnchecked(i), xOffset + thisX, yOffset, nextX - thisX, isWhitespace)); } } } int GlyphArrangement::insertEllipsis (const Font& font, const float maxXPos, const int startIndex, int endIndex) { int numDeleted = 0; if (glyphs.size() > 0) { Array dotGlyphs; Array dotXs; font.getGlyphPositions ("..", dotGlyphs, dotXs); const float dx = dotXs[1]; float xOffset = 0.0f, yOffset = 0.0f; while (endIndex > startIndex) { const PositionedGlyph& pg = glyphs.getReference (--endIndex); xOffset = pg.x; yOffset = pg.y; glyphs.remove (endIndex); ++numDeleted; if (xOffset + dx * 3 <= maxXPos) break; } for (int i = 3; --i >= 0;) { glyphs.insert (endIndex++, PositionedGlyph (font, '.', dotGlyphs.getFirst(), xOffset, yOffset, dx, false)); --numDeleted; xOffset += dx; if (xOffset > maxXPos) break; } } return numDeleted; } void GlyphArrangement::addJustifiedText (const Font& font, const String& text, float x, float y, const float maxLineWidth, Justification horizontalLayout) { int lineStartIndex = glyphs.size(); addLineOfText (font, text, x, y); const float originalY = y; while (lineStartIndex < glyphs.size()) { int i = lineStartIndex; if (glyphs.getReference(i).getCharacter() != '\n' && glyphs.getReference(i).getCharacter() != '\r') ++i; const float lineMaxX = glyphs.getReference (lineStartIndex).getLeft() + maxLineWidth; int lastWordBreakIndex = -1; while (i < glyphs.size()) { const PositionedGlyph& pg = glyphs.getReference (i); const juce_wchar c = pg.getCharacter(); if (c == '\r' || c == '\n') { ++i; if (c == '\r' && i < glyphs.size() && glyphs.getReference(i).getCharacter() == '\n') ++i; break; } if (pg.isWhitespace()) { lastWordBreakIndex = i + 1; } else if (pg.getRight() - 0.0001f >= lineMaxX) { if (lastWordBreakIndex >= 0) i = lastWordBreakIndex; break; } ++i; } const float currentLineStartX = glyphs.getReference (lineStartIndex).getLeft(); float currentLineEndX = currentLineStartX; for (int j = i; --j >= lineStartIndex;) { if (! glyphs.getReference (j).isWhitespace()) { currentLineEndX = glyphs.getReference (j).getRight(); break; } } float deltaX = 0.0f; if (horizontalLayout.testFlags (Justification::horizontallyJustified)) spreadOutLine (lineStartIndex, i - lineStartIndex, maxLineWidth); else if (horizontalLayout.testFlags (Justification::horizontallyCentred)) deltaX = (maxLineWidth - (currentLineEndX - currentLineStartX)) * 0.5f; else if (horizontalLayout.testFlags (Justification::right)) deltaX = maxLineWidth - (currentLineEndX - currentLineStartX); moveRangeOfGlyphs (lineStartIndex, i - lineStartIndex, x + deltaX - currentLineStartX, y - originalY); lineStartIndex = i; y += font.getHeight(); } } void GlyphArrangement::addFittedText (const Font& f, const String& text, const float x, const float y, const float width, const float height, Justification layout, int maximumLines, float minimumHorizontalScale) { if (minimumHorizontalScale == 0.0f) minimumHorizontalScale = Font::getDefaultMinimumHorizontalScaleFactor(); // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0 jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f); if (text.containsAnyOf ("\r\n")) { addLinesWithLineBreaks (text, f, x, y, width, height, layout); } else { const int startIndex = glyphs.size(); const String trimmed (text.trim()); addLineOfText (f, trimmed, x, y); const int numGlyphs = glyphs.size() - startIndex; if (numGlyphs > 0) { const float lineWidth = glyphs.getReference (glyphs.size() - 1).getRight() - glyphs.getReference (startIndex).getLeft(); if (lineWidth > 0) { if (lineWidth * minimumHorizontalScale < width) { if (lineWidth > width) stretchRangeOfGlyphs (startIndex, numGlyphs, width / lineWidth); justifyGlyphs (startIndex, numGlyphs, x, y, width, height, layout); } else if (maximumLines <= 1) { fitLineIntoSpace (startIndex, numGlyphs, x, y, width, height, f, layout, minimumHorizontalScale); } else { splitLines (trimmed, f, startIndex, x, y, width, height, maximumLines, lineWidth, layout, minimumHorizontalScale); } } } } } //============================================================================== void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, const float dx, const float dy) { jassert (startIndex >= 0); if (dx != 0.0f || dy != 0.0f) { if (num < 0 || startIndex + num > glyphs.size()) num = glyphs.size() - startIndex; while (--num >= 0) glyphs.getReference (startIndex++).moveBy (dx, dy); } } void GlyphArrangement::addLinesWithLineBreaks (const String& text, const Font& f, float x, float y, float width, float height, Justification layout) { GlyphArrangement ga; ga.addJustifiedText (f, text, x, y, width, layout); const Rectangle bb (ga.getBoundingBox (0, -1, false)); float dy = y - bb.getY(); if (layout.testFlags (Justification::verticallyCentred)) dy += (height - bb.getHeight()) * 0.5f; else if (layout.testFlags (Justification::bottom)) dy += (height - bb.getHeight()); ga.moveRangeOfGlyphs (0, -1, 0.0f, dy); glyphs.addArray (ga.glyphs); } int GlyphArrangement::fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font& font, Justification justification, float minimumHorizontalScale) { int numDeleted = 0; const float lineStartX = glyphs.getReference (start).getLeft(); float lineWidth = glyphs.getReference (start + numGlyphs - 1).getRight() - lineStartX; if (lineWidth > w) { if (minimumHorizontalScale < 1.0f) { stretchRangeOfGlyphs (start, numGlyphs, jmax (minimumHorizontalScale, w / lineWidth)); lineWidth = glyphs.getReference (start + numGlyphs - 1).getRight() - lineStartX - 0.5f; } if (lineWidth > w) { numDeleted = insertEllipsis (font, lineStartX + w, start, start + numGlyphs); numGlyphs -= numDeleted; } } justifyGlyphs (start, numGlyphs, x, y, w, h, justification); return numDeleted; } void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num, const float horizontalScaleFactor) { jassert (startIndex >= 0); if (num < 0 || startIndex + num > glyphs.size()) num = glyphs.size() - startIndex; if (num > 0) { const float xAnchor = glyphs.getReference (startIndex).getLeft(); while (--num >= 0) { PositionedGlyph& pg = glyphs.getReference (startIndex++); pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor; pg.font.setHorizontalScale (pg.font.getHorizontalScale() * horizontalScaleFactor); pg.w *= horizontalScaleFactor; } } } Rectangle GlyphArrangement::getBoundingBox (int startIndex, int num, const bool includeWhitespace) const { jassert (startIndex >= 0); if (num < 0 || startIndex + num > glyphs.size()) num = glyphs.size() - startIndex; Rectangle result; while (--num >= 0) { const PositionedGlyph& pg = glyphs.getReference (startIndex++); if (includeWhitespace || ! pg.isWhitespace()) result = result.getUnion (pg.getBounds()); } return result; } void GlyphArrangement::justifyGlyphs (const int startIndex, const int num, const float x, const float y, const float width, const float height, Justification justification) { jassert (num >= 0 && startIndex >= 0); if (glyphs.size() > 0 && num > 0) { const Rectangle bb (getBoundingBox (startIndex, num, ! justification.testFlags (Justification::horizontallyJustified | Justification::horizontallyCentred))); float deltaX = 0.0f, deltaY = 0.0f; if (justification.testFlags (Justification::horizontallyJustified)) deltaX = x - bb.getX(); else if (justification.testFlags (Justification::horizontallyCentred)) deltaX = x + (width - bb.getWidth()) * 0.5f - bb.getX(); else if (justification.testFlags (Justification::right)) deltaX = x + width - bb.getRight(); else deltaX = x - bb.getX(); if (justification.testFlags (Justification::top)) deltaY = y - bb.getY(); else if (justification.testFlags (Justification::bottom)) deltaY = y + height - bb.getBottom(); else deltaY = y + (height - bb.getHeight()) * 0.5f - bb.getY(); moveRangeOfGlyphs (startIndex, num, deltaX, deltaY); if (justification.testFlags (Justification::horizontallyJustified)) { int lineStart = 0; float baseY = glyphs.getReference (startIndex).getBaselineY(); int i; for (i = 0; i < num; ++i) { const float glyphY = glyphs.getReference (startIndex + i).getBaselineY(); if (glyphY != baseY) { spreadOutLine (startIndex + lineStart, i - lineStart, width); lineStart = i; baseY = glyphY; } } if (i > lineStart) spreadOutLine (startIndex + lineStart, i - lineStart, width); } } } void GlyphArrangement::spreadOutLine (const int start, const int num, const float targetWidth) { if (start + num < glyphs.size() && glyphs.getReference (start + num - 1).getCharacter() != '\r' && glyphs.getReference (start + num - 1).getCharacter() != '\n') { int numSpaces = 0; int spacesAtEnd = 0; for (int i = 0; i < num; ++i) { if (glyphs.getReference (start + i).isWhitespace()) { ++spacesAtEnd; ++numSpaces; } else { spacesAtEnd = 0; } } numSpaces -= spacesAtEnd; if (numSpaces > 0) { const float startX = glyphs.getReference (start).getLeft(); const float endX = glyphs.getReference (start + num - 1 - spacesAtEnd).getRight(); const float extraPaddingBetweenWords = (targetWidth - (endX - startX)) / (float) numSpaces; float deltaX = 0.0f; for (int i = 0; i < num; ++i) { glyphs.getReference (start + i).moveBy (deltaX, 0.0f); if (glyphs.getReference (start + i).isWhitespace()) deltaX += extraPaddingBetweenWords; } } } } void GlyphArrangement::splitLines (const String& text, Font font, int startIndex, float x, float y, float width, float height, int maximumLines, float lineWidth, Justification layout, float minimumHorizontalScale) { const int length = text.length(); const int originalStartIndex = startIndex; int numLines = 1; if (length <= 12 && ! text.containsAnyOf (" -\t\r\n")) maximumLines = 1; maximumLines = jmin (maximumLines, length); while (numLines < maximumLines) { ++numLines; const float newFontHeight = height / (float) numLines; if (newFontHeight < font.getHeight()) { font.setHeight (jmax (8.0f, newFontHeight)); removeRangeOfGlyphs (startIndex, -1); addLineOfText (font, text, x, y); lineWidth = glyphs.getReference (glyphs.size() - 1).getRight() - glyphs.getReference (startIndex).getLeft(); } // Try to estimate the point at which there are enough lines to fit the text, // allowing for unevenness in the lengths due to differently sized words. const float lineLengthUnevennessAllowance = 80.0f; if (numLines > (lineWidth + lineLengthUnevennessAllowance) / width || newFontHeight < 8.0f) break; } if (numLines < 1) numLines = 1; float lineY = y; float widthPerLine = lineWidth / numLines; for (int line = 0; line < numLines; ++line) { int i = startIndex; float lineStartX = glyphs.getReference (startIndex).getLeft(); if (line == numLines - 1) { widthPerLine = width; i = glyphs.size(); } else { while (i < glyphs.size()) { lineWidth = (glyphs.getReference (i).getRight() - lineStartX); if (lineWidth > widthPerLine) { // got to a point where the line's too long, so skip forward to find a // good place to break it.. const int searchStartIndex = i; while (i < glyphs.size()) { if ((glyphs.getReference (i).getRight() - lineStartX) * minimumHorizontalScale < width) { if (glyphs.getReference (i).isWhitespace() || glyphs.getReference (i).getCharacter() == '-') { ++i; break; } } else { // can't find a suitable break, so try looking backwards.. i = searchStartIndex; for (int back = 1; back < jmin (7, i - startIndex - 1); ++back) { if (glyphs.getReference (i - back).isWhitespace() || glyphs.getReference (i - back).getCharacter() == '-') { i -= back - 1; break; } } break; } ++i; } break; } ++i; } int wsStart = i; while (wsStart > 0 && glyphs.getReference (wsStart - 1).isWhitespace()) --wsStart; int wsEnd = i; while (wsEnd < glyphs.size() && glyphs.getReference (wsEnd).isWhitespace()) ++wsEnd; removeRangeOfGlyphs (wsStart, wsEnd - wsStart); i = jmax (wsStart, startIndex + 1); } i -= fitLineIntoSpace (startIndex, i - startIndex, x, lineY, width, font.getHeight(), font, layout.getOnlyHorizontalFlags() | Justification::verticallyCentred, minimumHorizontalScale); startIndex = i; lineY += font.getHeight(); if (startIndex >= glyphs.size()) break; } justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex, x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified); } //============================================================================== void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGlyph& pg, const int i, const AffineTransform& transform) const { const float lineThickness = (pg.font.getDescent()) * 0.3f; float nextX = pg.x + pg.w; if (i < glyphs.size() - 1 && glyphs.getReference (i + 1).y == pg.y) nextX = glyphs.getReference (i + 1).x; Path p; p.addRectangle (pg.x, pg.y + lineThickness * 2.0f, nextX - pg.x, lineThickness); g.fillPath (p, transform); } void GlyphArrangement::draw (const Graphics& g) const { draw (g, AffineTransform()); } void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const { LowLevelGraphicsContext& context = g.getInternalContext(); Font lastFont (context.getFont()); bool needToRestore = false; for (int i = 0; i < glyphs.size(); ++i) { const PositionedGlyph& pg = glyphs.getReference(i); if (pg.font.isUnderlined()) drawGlyphUnderline (g, pg, i, transform); if (! pg.isWhitespace()) { if (lastFont != pg.font) { lastFont = pg.font; if (! needToRestore) { needToRestore = true; context.saveState(); } context.setFont (lastFont); } context.drawGlyph (pg.glyph, AffineTransform::translation (pg.x, pg.y).followedBy (transform)); } } if (needToRestore) context.restoreState(); } void GlyphArrangement::createPath (Path& path) const { for (int i = 0; i < glyphs.size(); ++i) glyphs.getReference (i).createPath (path); } int GlyphArrangement::findGlyphIndexAt (const float x, const float y) const { for (int i = 0; i < glyphs.size(); ++i) if (glyphs.getReference (i).hitTest (x, y)) return i; return -1; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.h000066400000000000000000000340371320201440200322000ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_GLYPHARRANGEMENT_H_INCLUDED #define JUCE_GLYPHARRANGEMENT_H_INCLUDED //============================================================================== /** A glyph from a particular font, with a particular size, style, typeface and position. You should rarely need to use this class directly - for most purposes, the GlyphArrangement class will do what you need for text layout. @see GlyphArrangement, Font */ class JUCE_API PositionedGlyph { public: //============================================================================== PositionedGlyph() noexcept; PositionedGlyph (const Font& font, juce_wchar character, int glyphNumber, float anchorX, float baselineY, float width, bool isWhitespace); PositionedGlyph (const PositionedGlyph&); PositionedGlyph& operator= (const PositionedGlyph&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS PositionedGlyph (PositionedGlyph&&) noexcept; PositionedGlyph& operator= (PositionedGlyph&&) noexcept; #endif ~PositionedGlyph(); /** Returns the character the glyph represents. */ juce_wchar getCharacter() const noexcept { return character; } /** Checks whether the glyph is actually empty. */ bool isWhitespace() const noexcept { return whitespace; } /** Returns the position of the glyph's left-hand edge. */ float getLeft() const noexcept { return x; } /** Returns the position of the glyph's right-hand edge. */ float getRight() const noexcept { return x + w; } /** Returns the y position of the glyph's baseline. */ float getBaselineY() const noexcept { return y; } /** Returns the y position of the top of the glyph. */ float getTop() const { return y - font.getAscent(); } /** Returns the y position of the bottom of the glyph. */ float getBottom() const { return y + font.getDescent(); } /** Returns the bounds of the glyph. */ Rectangle getBounds() const { return Rectangle (x, getTop(), w, font.getHeight()); } //============================================================================== /** Shifts the glyph's position by a relative amount. */ void moveBy (float deltaX, float deltaY); //============================================================================== /** Draws the glyph into a graphics context. (Note that this may change the context's currently selected font). */ void draw (Graphics& g) const; /** Draws the glyph into a graphics context, with an extra transform applied to it. (Note that this may change the context's currently selected font). */ void draw (Graphics& g, const AffineTransform& transform) const; /** Returns the path for this glyph. @param path the glyph's outline will be appended to this path */ void createPath (Path& path) const; /** Checks to see if a point lies within this glyph. */ bool hitTest (float x, float y) const; private: //============================================================================== friend class GlyphArrangement; Font font; juce_wchar character; int glyph; float x, y, w; bool whitespace; JUCE_LEAK_DETECTOR (PositionedGlyph) }; //============================================================================== /** A set of glyphs, each with a position. You can create a GlyphArrangement, text to it and then draw it onto a graphics context. It's used internally by the text methods in the Graphics class, but can be used directly if more control is needed. @see Font, PositionedGlyph */ class JUCE_API GlyphArrangement { public: //============================================================================== /** Creates an empty arrangement. */ GlyphArrangement(); /** Takes a copy of another arrangement. */ GlyphArrangement (const GlyphArrangement&); /** Copies another arrangement onto this one. To add another arrangement without clearing this one, use addGlyphArrangement(). */ GlyphArrangement& operator= (const GlyphArrangement&); /** Destructor. */ ~GlyphArrangement(); //============================================================================== /** Returns the total number of glyphs in the arrangement. */ int getNumGlyphs() const noexcept { return glyphs.size(); } /** Returns one of the glyphs from the arrangement. @param index the glyph's index, from 0 to (getNumGlyphs() - 1). Be careful not to pass an out-of-range index here, as it doesn't do any bounds-checking. */ PositionedGlyph& getGlyph (int index) const noexcept; //============================================================================== /** Clears all text from the arrangement and resets it. */ void clear(); /** Appends a line of text to the arrangement. This will add the text as a single line, where x is the left-hand edge of the first character, and y is the position for the text's baseline. If the text contains new-lines or carriage-returns, this will ignore them - use addJustifiedText() to add multi-line arrangements. */ void addLineOfText (const Font& font, const String& text, float x, float y); /** Adds a line of text, truncating it if it's wider than a specified size. This is the same as addLineOfText(), but if the line's width exceeds the value specified in maxWidthPixels, it will be truncated using either ellipsis (i.e. dots: "..."), if useEllipsis is true, or if this is false, it will just drop any subsequent characters. */ void addCurtailedLineOfText (const Font& font, const String& text, float x, float y, float maxWidthPixels, bool useEllipsis); /** Adds some multi-line text, breaking lines at word-boundaries if they are too wide. This will add text to the arrangement, breaking it into new lines either where there is a new-line or carriage-return character in the text, or where a line's width exceeds the value set in maxLineWidth. Each line that is added will be laid out using the flags set in horizontalLayout, so the lines can be left- or right-justified, or centred horizontally in the space between x and (x + maxLineWidth). The y coordinate is the position of the baseline of the first line of text - subsequent lines will be placed below it, separated by a distance of font.getHeight(). */ void addJustifiedText (const Font& font, const String& text, float x, float y, float maxLineWidth, Justification horizontalLayout); /** Tries to fit some text withing a given space. This does its best to make the given text readable within the specified rectangle, so it useful for labelling things. If the text is too big, it'll be squashed horizontally or broken over multiple lines if the maximumLinesToUse value allows this. If the text just won't fit into the space, it'll cram as much as possible in there, and put some ellipsis at the end to show that it's been truncated. A Justification parameter lets you specify how the text is laid out within the rectangle, both horizontally and vertically. The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you can set this value to 1.0f. Pass 0 if you want it to use the default value. @see Graphics::drawFittedText */ void addFittedText (const Font& font, const String& text, float x, float y, float width, float height, Justification layout, int maximumLinesToUse, float minimumHorizontalScale = 0.0f); /** Appends another glyph arrangement to this one. */ void addGlyphArrangement (const GlyphArrangement&); /** Appends a custom glyph to the arrangement. */ void addGlyph (const PositionedGlyph&); //============================================================================== /** Draws this glyph arrangement to a graphics context. This uses cached bitmaps so is much faster than the draw (Graphics&, const AffineTransform&) method, which renders the glyphs as filled vectors. */ void draw (const Graphics&) const; /** Draws this glyph arrangement to a graphics context. This renders the paths as filled vectors, so is far slower than the draw (Graphics&) method for non-transformed arrangements. */ void draw (const Graphics&, const AffineTransform&) const; /** Converts the set of glyphs into a path. @param path the glyphs' outlines will be appended to this path */ void createPath (Path& path) const; /** Looks for a glyph that contains the given coordinate. @returns the index of the glyph, or -1 if none were found. */ int findGlyphIndexAt (float x, float y) const; //============================================================================== /** Finds the smallest rectangle that will enclose a subset of the glyphs. @param startIndex the first glyph to test @param numGlyphs the number of glyphs to include; if this is < 0, all glyphs after startIndex will be included @param includeWhitespace if true, the extent of any whitespace characters will also be taken into account */ Rectangle getBoundingBox (int startIndex, int numGlyphs, bool includeWhitespace) const; /** Shifts a set of glyphs by a given amount. @param startIndex the first glyph to transform @param numGlyphs the number of glyphs to move; if this is < 0, all glyphs after startIndex will be used @param deltaX the amount to add to their x-positions @param deltaY the amount to add to their y-positions */ void moveRangeOfGlyphs (int startIndex, int numGlyphs, float deltaX, float deltaY); /** Removes a set of glyphs from the arrangement. @param startIndex the first glyph to remove @param numGlyphs the number of glyphs to remove; if this is < 0, all glyphs after startIndex will be deleted */ void removeRangeOfGlyphs (int startIndex, int numGlyphs); /** Expands or compresses a set of glyphs horizontally. @param startIndex the first glyph to transform @param numGlyphs the number of glyphs to stretch; if this is < 0, all glyphs after startIndex will be used @param horizontalScaleFactor how much to scale their horizontal width by */ void stretchRangeOfGlyphs (int startIndex, int numGlyphs, float horizontalScaleFactor); /** Justifies a set of glyphs within a given space. This moves the glyphs as a block so that the whole thing is located within the given rectangle with the specified layout. If the Justification::horizontallyJustified flag is specified, each line will be stretched out to fill the specified width. */ void justifyGlyphs (int startIndex, int numGlyphs, float x, float y, float width, float height, Justification justification); private: //============================================================================== Array glyphs; int insertEllipsis (const Font&, float maxXPos, int startIndex, int endIndex); int fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font&, Justification, float minimumHorizontalScale); void spreadOutLine (int start, int numGlyphs, float targetWidth); void splitLines (const String&, Font, int start, float x, float y, float w, float h, int maxLines, float lineWidth, Justification, float minimumHorizontalScale); void addLinesWithLineBreaks (const String&, const Font&, float x, float y, float width, float height, Justification); void drawGlyphUnderline (const Graphics&, const PositionedGlyph&, int, const AffineTransform&) const; JUCE_LEAK_DETECTOR (GlyphArrangement) }; #endif // JUCE_GLYPHARRANGEMENT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp000066400000000000000000000512651320201440200314100ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ TextLayout::Glyph::Glyph (const int glyph, Point anch, float w) noexcept : glyphCode (glyph), anchor (anch), width (w) { } TextLayout::Glyph::Glyph (const Glyph& other) noexcept : glyphCode (other.glyphCode), anchor (other.anchor), width (other.width) { } TextLayout::Glyph& TextLayout::Glyph::operator= (const Glyph& other) noexcept { glyphCode = other.glyphCode; anchor = other.anchor; width = other.width; return *this; } TextLayout::Glyph::~Glyph() noexcept {} //============================================================================== TextLayout::Run::Run() noexcept : colour (0xff000000) { } TextLayout::Run::Run (Range range, const int numGlyphsToPreallocate) : colour (0xff000000), stringRange (range) { glyphs.ensureStorageAllocated (numGlyphsToPreallocate); } TextLayout::Run::Run (const Run& other) : font (other.font), colour (other.colour), glyphs (other.glyphs), stringRange (other.stringRange) { } TextLayout::Run::~Run() noexcept {} //============================================================================== TextLayout::Line::Line() noexcept : ascent (0.0f), descent (0.0f), leading (0.0f) { } TextLayout::Line::Line (Range range, Point o, float asc, float desc, float lead, int numRunsToPreallocate) : stringRange (range), lineOrigin (o), ascent (asc), descent (desc), leading (lead) { runs.ensureStorageAllocated (numRunsToPreallocate); } TextLayout::Line::Line (const Line& other) : stringRange (other.stringRange), lineOrigin (other.lineOrigin), ascent (other.ascent), descent (other.descent), leading (other.leading) { runs.addCopiesOf (other.runs); } TextLayout::Line::~Line() noexcept { } Range TextLayout::Line::getLineBoundsX() const noexcept { Range range; bool isFirst = true; for (int i = runs.size(); --i >= 0;) { const Run& run = *runs.getUnchecked(i); if (run.glyphs.size() > 0) { float minX = run.glyphs.getReference(0).anchor.x; float maxX = minX; for (int j = run.glyphs.size(); --j >= 0;) { const Glyph& glyph = run.glyphs.getReference (j); const float x = glyph.anchor.x; minX = jmin (minX, x); maxX = jmax (maxX, x + glyph.width); } if (isFirst) { isFirst = false; range = Range (minX, maxX); } else { range = range.getUnionWith (Range (minX, maxX)); } } } return range + lineOrigin.x; } Range TextLayout::Line::getLineBoundsY() const noexcept { return Range (lineOrigin.y - ascent, lineOrigin.y + descent); } Rectangle TextLayout::Line::getLineBounds() const noexcept { const Range x (getLineBoundsX()), y (getLineBoundsY()); return Rectangle (x.getStart(), y.getStart(), x.getLength(), y.getLength()); } //============================================================================== TextLayout::TextLayout() : width (0), height (0), justification (Justification::topLeft) { } TextLayout::TextLayout (const TextLayout& other) : width (other.width), height (other.height), justification (other.justification) { lines.addCopiesOf (other.lines); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS TextLayout::TextLayout (TextLayout&& other) noexcept : lines (static_cast&&> (other.lines)), width (other.width), height (other.height), justification (other.justification) { } TextLayout& TextLayout::operator= (TextLayout&& other) noexcept { lines = static_cast&&> (other.lines); width = other.width; height = other.height; justification = other.justification; return *this; } #endif TextLayout& TextLayout::operator= (const TextLayout& other) { width = other.width; height = other.height; justification = other.justification; lines.clear(); lines.addCopiesOf (other.lines); return *this; } TextLayout::~TextLayout() { } TextLayout::Line& TextLayout::getLine (const int index) const { return *lines.getUnchecked (index); } void TextLayout::ensureStorageAllocated (int numLinesNeeded) { lines.ensureStorageAllocated (numLinesNeeded); } void TextLayout::addLine (Line* line) { lines.add (line); } void TextLayout::draw (Graphics& g, const Rectangle& area) const { const Point origin (justification.appliedToRectangle (Rectangle (width, getHeight()), area).getPosition()); LowLevelGraphicsContext& context = g.getInternalContext(); for (int i = 0; i < lines.size(); ++i) { const Line& line = getLine (i); const Point lineOrigin (origin + line.lineOrigin); for (int j = 0; j < line.runs.size(); ++j) { const Run& run = *line.runs.getUnchecked (j); context.setFont (run.font); context.setFill (run.colour); for (int k = 0; k < run.glyphs.size(); ++k) { const Glyph& glyph = run.glyphs.getReference (k); context.drawGlyph (glyph.glyphCode, AffineTransform::translation (lineOrigin.x + glyph.anchor.x, lineOrigin.y + glyph.anchor.y)); } } } } void TextLayout::createLayout (const AttributedString& text, float maxWidth) { createLayout (text, maxWidth, 1.0e7f); } void TextLayout::createLayout (const AttributedString& text, float maxWidth, float maxHeight) { lines.clear(); width = maxWidth; height = maxHeight; justification = text.getJustification(); if (! createNativeLayout (text)) createStandardLayout (text); recalculateSize (text); } void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth) { createLayoutWithBalancedLineLengths (text, maxWidth, 1.0e7f); } void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth, float maxHeight) { const float minimumWidth = maxWidth / 2.0f; float bestWidth = maxWidth; float bestLineProportion = 0.0f; while (maxWidth > minimumWidth) { createLayout (text, maxWidth, maxHeight); if (getNumLines() < 2) return; const float line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength(); const float line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength(); const float shortestLine = jmin (line1, line2); const float prop = (shortestLine > 0) ? jmax (line1, line2) / shortestLine : 1.0f; if (prop > 0.9f) return; if (prop > bestLineProportion) { bestLineProportion = prop; bestWidth = maxWidth; } maxWidth -= 10.0f; } if (bestWidth != maxWidth) createLayout (text, bestWidth, maxHeight); } //============================================================================== namespace TextLayoutHelpers { struct FontAndColour { FontAndColour (const Font* f) noexcept : font (f), colour (0xff000000) {} const Font* font; Colour colour; bool operator!= (const FontAndColour& other) const noexcept { return (font != other.font && *font != *other.font) || colour != other.colour; } }; struct RunAttribute { RunAttribute (const FontAndColour& fc, const Range r) noexcept : fontAndColour (fc), range (r) {} FontAndColour fontAndColour; Range range; }; struct Token { Token (const String& t, const Font& f, Colour c, const bool whitespace) : text (t), font (f), colour (c), area (font.getStringWidthFloat (t), f.getHeight()), isWhitespace (whitespace), isNewLine (t.containsChar ('\n') || t.containsChar ('\r')) {} const String text; const Font font; const Colour colour; Rectangle area; int line; float lineHeight; const bool isWhitespace, isNewLine; private: Token& operator= (const Token&); }; class TokenList { public: TokenList() noexcept : totalLines (0) {} void createLayout (const AttributedString& text, TextLayout& layout) { tokens.ensureStorageAllocated (64); layout.ensureStorageAllocated (totalLines); addTextRuns (text); layoutRuns (layout.getWidth()); int charPosition = 0; int lineStartPosition = 0; int runStartPosition = 0; ScopedPointer currentLine; ScopedPointer currentRun; bool needToSetLineOrigin = true; for (int i = 0; i < tokens.size(); ++i) { const Token& t = *tokens.getUnchecked (i); Array newGlyphs; Array xOffsets; t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text), newGlyphs, xOffsets); if (currentRun == nullptr) currentRun = new TextLayout::Run(); if (currentLine == nullptr) currentLine = new TextLayout::Line(); if (newGlyphs.size() > 0) { currentRun->glyphs.ensureStorageAllocated (currentRun->glyphs.size() + newGlyphs.size()); const Point tokenOrigin (t.area.getPosition().translated (0, t.font.getAscent())); if (needToSetLineOrigin) { needToSetLineOrigin = false; currentLine->lineOrigin = tokenOrigin; } const Point glyphOffset (tokenOrigin - currentLine->lineOrigin); for (int j = 0; j < newGlyphs.size(); ++j) { const float x = xOffsets.getUnchecked (j); currentRun->glyphs.add (TextLayout::Glyph (newGlyphs.getUnchecked(j), glyphOffset.translated (x, 0), xOffsets.getUnchecked (j + 1) - x)); } charPosition += newGlyphs.size(); } if (t.isWhitespace || t.isNewLine) ++charPosition; const Token* const nextToken = tokens [i + 1]; if (nextToken == nullptr) // this is the last token { addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition); currentLine->stringRange = Range (lineStartPosition, charPosition); if (! needToSetLineOrigin) layout.addLine (currentLine.release()); needToSetLineOrigin = true; } else { if (t.font != nextToken->font || t.colour != nextToken->colour) { addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition); runStartPosition = charPosition; } if (t.line != nextToken->line) { if (currentRun == nullptr) currentRun = new TextLayout::Run(); addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition); currentLine->stringRange = Range (lineStartPosition, charPosition); if (! needToSetLineOrigin) layout.addLine (currentLine.release()); runStartPosition = charPosition; lineStartPosition = charPosition; needToSetLineOrigin = true; } } } if ((text.getJustification().getFlags() & (Justification::right | Justification::horizontallyCentred)) != 0) { const float totalW = layout.getWidth(); const bool isCentred = (text.getJustification().getFlags() & Justification::horizontallyCentred) != 0; for (int i = 0; i < layout.getNumLines(); ++i) { float dx = totalW - layout.getLine(i).getLineBoundsX().getLength(); if (isCentred) dx /= 2.0f; layout.getLine(i).lineOrigin.x += dx; } } } private: static void addRun (TextLayout::Line& glyphLine, TextLayout::Run* glyphRun, const Token& t, const int start, const int end) { glyphRun->stringRange = Range (start, end); glyphRun->font = t.font; glyphRun->colour = t.colour; glyphLine.ascent = jmax (glyphLine.ascent, t.font.getAscent()); glyphLine.descent = jmax (glyphLine.descent, t.font.getDescent()); glyphLine.runs.add (glyphRun); } static int getCharacterType (const juce_wchar c) noexcept { if (c == '\r' || c == '\n') return 0; return CharacterFunctions::isWhitespace (c) ? 2 : 1; } void appendText (const AttributedString& text, const Range stringRange, const Font& font, Colour colour) { const String stringText (text.getText().substring (stringRange.getStart(), stringRange.getEnd())); String::CharPointerType t (stringText.getCharPointer()); String currentString; int lastCharType = 0; for (;;) { const juce_wchar c = t.getAndAdvance(); if (c == 0) break; const int charType = getCharacterType (c); if (charType == 0 || charType != lastCharType) { if (currentString.isNotEmpty()) tokens.add (new Token (currentString, font, colour, lastCharType == 2 || lastCharType == 0)); currentString = String::charToString (c); if (c == '\r' && *t == '\n') currentString += t.getAndAdvance(); } else { currentString += c; } lastCharType = charType; } if (currentString.isNotEmpty()) tokens.add (new Token (currentString, font, colour, lastCharType == 2)); } void layoutRuns (const float maxWidth) { float x = 0, y = 0, h = 0; int i; for (i = 0; i < tokens.size(); ++i) { Token& t = *tokens.getUnchecked(i); t.area.setPosition (x, y); t.line = totalLines; x += t.area.getWidth(); h = jmax (h, t.area.getHeight()); const Token* const nextTok = tokens[i + 1]; if (nextTok == nullptr) break; if (t.isNewLine || ((! nextTok->isWhitespace) && x + nextTok->area.getWidth() > maxWidth)) { setLastLineHeight (i + 1, h); x = 0; y += h; h = 0; ++totalLines; } } setLastLineHeight (jmin (i + 1, tokens.size()), h); ++totalLines; } void setLastLineHeight (int i, const float height) noexcept { while (--i >= 0) { Token& tok = *tokens.getUnchecked (i); if (tok.line == totalLines) tok.lineHeight = height; else break; } } void addTextRuns (const AttributedString& text) { Font defaultFont; Array runAttributes; { const int stringLength = text.getText().length(); int rangeStart = 0; FontAndColour lastFontAndColour (&defaultFont); // Iterate through every character in the string for (int i = 0; i < stringLength; ++i) { FontAndColour newFontAndColour (&defaultFont); const int numCharacterAttributes = text.getNumAttributes(); for (int j = 0; j < numCharacterAttributes; ++j) { const AttributedString::Attribute& attr = *text.getAttribute (j); if (attr.range.contains (i)) { if (const Font* f = attr.getFont()) newFontAndColour.font = f; if (const Colour* c = attr.getColour()) newFontAndColour.colour = *c; } } if (i > 0 && newFontAndColour != lastFontAndColour) { runAttributes.add (RunAttribute (lastFontAndColour, Range (rangeStart, i))); rangeStart = i; } lastFontAndColour = newFontAndColour; } if (rangeStart < stringLength) runAttributes.add (RunAttribute (lastFontAndColour, Range (rangeStart, stringLength))); } for (int i = 0; i < runAttributes.size(); ++i) { const RunAttribute& r = runAttributes.getReference(i); appendText (text, r.range, *(r.fontAndColour.font), r.fontAndColour.colour); } } static String getTrimmedEndIfNotAllWhitespace (const String& s) { String trimmed (s.trimEnd()); if (trimmed.isEmpty() && ! s.isEmpty()) trimmed = s.replaceCharacters ("\r\n\t", " "); return trimmed; } OwnedArray tokens; int totalLines; JUCE_DECLARE_NON_COPYABLE (TokenList) }; } //============================================================================== void TextLayout::createStandardLayout (const AttributedString& text) { TextLayoutHelpers::TokenList l; l.createLayout (text, *this); } void TextLayout::recalculateSize (const AttributedString& text) { if (lines.size() > 0 && text.getReadingDirection() != AttributedString::rightToLeft) { Rectangle bounds (lines.getFirst()->getLineBounds()); for (int i = lines.size(); --i > 0;) bounds = bounds.getUnion (lines.getUnchecked(i)->getLineBounds()); for (int i = lines.size(); --i >= 0;) lines.getUnchecked(i)->lineOrigin.x -= bounds.getX(); width = bounds.getWidth(); height = bounds.getHeight(); } else { width = 0; height = 0; } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.h000066400000000000000000000163051320201440200310510ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_TEXTLAYOUT_H_INCLUDED #define JUCE_TEXTLAYOUT_H_INCLUDED //============================================================================== /** A Pre-formatted piece of text, which may contain multiple fonts and colours. A TextLayout is created from an AttributedString, and once created can be quickly drawn into a Graphics context. @see AttributedString */ class JUCE_API TextLayout { public: /** Creates an empty layout. Having created a TextLayout, you can populate it using createLayout() or createLayoutWithBalancedLineLengths(). */ TextLayout(); TextLayout (const TextLayout&); TextLayout& operator= (const TextLayout&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS TextLayout (TextLayout&&) noexcept; TextLayout& operator= (TextLayout&&) noexcept; #endif /** Destructor. */ ~TextLayout(); //============================================================================== /** Creates a layout from the given attributed string. This will replace any data that is currently stored in the layout. */ void createLayout (const AttributedString&, float maxWidth); /** Creates a layout from the given attributed string, given some size constraints. This will replace any data that is currently stored in the layout. */ void createLayout (const AttributedString&, float maxWidth, float maxHeight); /** Creates a layout, attempting to choose a width which results in lines of a similar length. This will be slower than the normal createLayout method, but produces a tidier result. */ void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth); /** Creates a layout, attempting to choose a width which results in lines of a similar length. This will be slower than the normal createLayout method, but produces a tidier result. */ void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth, float maxHeight); /** Draws the layout within the specified area. The position of the text within the rectangle is controlled by the justification flags set in the original AttributedString that was used to create this layout. */ void draw (Graphics&, const Rectangle& area) const; //============================================================================== /** A positioned glyph. */ class JUCE_API Glyph { public: Glyph (int glyphCode, Point anchor, float width) noexcept; Glyph (const Glyph&) noexcept; Glyph& operator= (const Glyph&) noexcept; ~Glyph() noexcept; /** The code number of this glyph. */ int glyphCode; /** The glyph's anchor point - this is relative to the line's origin. @see TextLayout::Line::lineOrigin */ Point anchor; float width; private: JUCE_LEAK_DETECTOR (Glyph) }; //============================================================================== /** A sequence of glyphs with a common font and colour. */ class JUCE_API Run { public: Run() noexcept; Run (const Run&); Run (Range stringRange, int numGlyphsToPreallocate); ~Run() noexcept; Font font; /**< The run's font. */ Colour colour; /**< The run's colour. */ Array glyphs; /**< The glyphs in this run. */ Range stringRange; /**< The character range that this run represents in the original string that was used to create it. */ private: Run& operator= (const Run&); JUCE_LEAK_DETECTOR (Run) }; //============================================================================== /** A line containing a sequence of glyph-runs. */ class JUCE_API Line { public: Line() noexcept; Line (const Line&); Line (Range stringRange, Point lineOrigin, float ascent, float descent, float leading, int numRunsToPreallocate); ~Line() noexcept; /** Returns the X position range which contains all the glyphs in this line. */ Range getLineBoundsX() const noexcept; /** Returns the Y position range which contains all the glyphs in this line. */ Range getLineBoundsY() const noexcept; /** Returns the smallest rectangle which contains all the glyphs in this line. */ Rectangle getLineBounds() const noexcept; OwnedArray runs; /**< The glyph-runs in this line. */ Range stringRange; /**< The character range that this line represents in the original string that was used to create it. */ Point lineOrigin; /**< The line's baseline origin. */ float ascent, descent, leading; private: Line& operator= (const Line&); JUCE_LEAK_DETECTOR (Line) }; //============================================================================== /** Returns the maximum width of the content. */ float getWidth() const noexcept { return width; } /** Returns the maximum height of the content. */ float getHeight() const noexcept { return height; } /** Returns the number of lines in the layout. */ int getNumLines() const noexcept { return lines.size(); } /** Returns one of the lines. */ Line& getLine (int index) const; /** Adds a line to the layout. The layout will take ownership of this line object and will delete it when it is no longer needed. */ void addLine (Line*); /** Pre-allocates space for the specified number of lines. */ void ensureStorageAllocated (int numLinesNeeded); private: OwnedArray lines; float width, height; Justification justification; void createStandardLayout (const AttributedString&); bool createNativeLayout (const AttributedString&); void recalculateSize (const AttributedString&); JUCE_LEAK_DETECTOR (TextLayout) }; #endif // JUCE_TEXTLAYOUT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp000066400000000000000000000214661320201440200310260ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct FontStyleHelpers { static const char* getStyleName (const bool bold, const bool italic) noexcept { if (bold && italic) return "Bold Italic"; if (bold) return "Bold"; if (italic) return "Italic"; return "Regular"; } static const char* getStyleName (const int styleFlags) noexcept { return getStyleName ((styleFlags & Font::bold) != 0, (styleFlags & Font::italic) != 0); } static bool isBold (const String& style) noexcept { return style.containsWholeWordIgnoreCase ("Bold"); } static bool isItalic (const String& style) noexcept { return style.containsWholeWordIgnoreCase ("Italic") || style.containsWholeWordIgnoreCase ("Oblique"); } static bool isPlaceholderFamilyName (const String& family) { return family == Font::getDefaultSansSerifFontName() || family == Font::getDefaultSerifFontName() || family == Font::getDefaultMonospacedFontName(); } struct ConcreteFamilyNames { ConcreteFamilyNames() : sans (findName (Font::getDefaultSansSerifFontName())), serif (findName (Font::getDefaultSerifFontName())), mono (findName (Font::getDefaultMonospacedFontName())) { } String lookUp (const String& placeholder) { if (placeholder == Font::getDefaultSansSerifFontName()) return sans; if (placeholder == Font::getDefaultSerifFontName()) return serif; if (placeholder == Font::getDefaultMonospacedFontName()) return mono; return findName (placeholder); } private: static String findName (const String& placeholder) { const Font f (placeholder, Font::getDefaultStyle(), 15.0f); return Font::getDefaultTypefaceForFont (f)->getName(); } String sans, serif, mono; }; static String getConcreteFamilyNameFromPlaceholder (const String& placeholder) { static ConcreteFamilyNames names; return names.lookUp (placeholder); } static String getConcreteFamilyName (const Font& font) { const String& family = font.getTypefaceName(); return isPlaceholderFamilyName (family) ? getConcreteFamilyNameFromPlaceholder (family) : family; } }; //============================================================================== Typeface::Typeface (const String& faceName, const String& styleName) noexcept : name (faceName), style (styleName) { } Typeface::~Typeface() { } Typeface::Ptr Typeface::getFallbackTypeface() { const Font fallbackFont (Font::getFallbackFontName(), Font::getFallbackFontStyle(), 10.0f); return fallbackFont.getTypeface(); } EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) { Path path; if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) { applyVerticalHintingTransform (fontHeight, path); return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), path, transform); } return nullptr; } //============================================================================== struct Typeface::HintingParams { HintingParams (Typeface& t) : cachedSize (0), top (0), middle (0), bottom (0) { Font font (&t); font = font.withHeight ((float) standardHeight); top = getAverageY (font, "BDEFPRTZOQ", true); middle = getAverageY (font, "acegmnopqrsuvwxy", true); bottom = getAverageY (font, "BDELZOC", false); } void applyVerticalHintingTransform (float fontSize, Path& path) { if (cachedSize != fontSize) { cachedSize = fontSize; cachedScale = Scaling (top, middle, bottom, fontSize); } if (bottom < top + 3.0f / fontSize) return; Path result; for (Path::Iterator i (path); i.next();) { switch (i.elementType) { case Path::Iterator::startNewSubPath: result.startNewSubPath (i.x1, cachedScale.apply (i.y1)); break; case Path::Iterator::lineTo: result.lineTo (i.x1, cachedScale.apply (i.y1)); break; case Path::Iterator::quadraticTo: result.quadraticTo (i.x1, cachedScale.apply (i.y1), i.x2, cachedScale.apply (i.y2)); break; case Path::Iterator::cubicTo: result.cubicTo (i.x1, cachedScale.apply (i.y1), i.x2, cachedScale.apply (i.y2), i.x3, cachedScale.apply (i.y3)); break; case Path::Iterator::closePath: result.closeSubPath(); break; default: jassertfalse; break; } } result.swapWithPath (path); } private: struct Scaling { Scaling() noexcept : middle(), upperScale(), upperOffset(), lowerScale(), lowerOffset() {} Scaling (float t, float m, float b, float fontSize) noexcept : middle (m) { const float newT = std::floor (fontSize * t + 0.5f) / fontSize; const float newB = std::floor (fontSize * b + 0.5f) / fontSize; const float newM = std::floor (fontSize * m + 0.3f) / fontSize; // this is slightly biased so that lower-case letters // are more likely to become taller than shorter. upperScale = jlimit (0.9f, 1.1f, (newM - newT) / (m - t)); lowerScale = jlimit (0.9f, 1.1f, (newB - newM) / (b - m)); upperOffset = newM - m * upperScale; lowerOffset = newB - b * lowerScale; } float apply (float y) const noexcept { return y < middle ? (y * upperScale + upperOffset) : (y * lowerScale + lowerOffset); } float middle, upperScale, upperOffset, lowerScale, lowerOffset; }; float cachedSize; Scaling cachedScale; static float getAverageY (const Font& font, const char* chars, bool getTop) { GlyphArrangement ga; ga.addLineOfText (font, chars, 0, 0); Array y; DefaultElementComparator sorter; for (int i = 0; i < ga.getNumGlyphs(); ++i) { Path p; ga.getGlyph (i).createPath (p); Rectangle bounds (p.getBounds()); if (! p.isEmpty()) y.addSorted (sorter, getTop ? bounds.getY() : bounds.getBottom()); } float median = y[y.size() / 2]; float total = 0; int num = 0; for (int i = 0; i < y.size(); ++i) { if (std::abs (median - y.getUnchecked(i)) < 0.05f * (float) standardHeight) { total += y.getUnchecked(i); ++num; } } return num < 4 ? 0.0f : total / (num * (float) standardHeight); } enum { standardHeight = 100 }; float top, middle, bottom; }; void Typeface::applyVerticalHintingTransform (float fontSize, Path& path) { if (fontSize > 3.0f && fontSize < 25.0f) { ScopedLock sl (hintingLock); if (hintingParams == nullptr) hintingParams = new HintingParams (*this); return hintingParams->applyVerticalHintingTransform (fontSize, path); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h000066400000000000000000000151701320201440200304660ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_TYPEFACE_H_INCLUDED #define JUCE_TYPEFACE_H_INCLUDED //============================================================================== /** A typeface represents a size-independent font. This base class is abstract, but calling createSystemTypefaceFor() will return a platform-specific subclass that can be used. The CustomTypeface subclass allow you to build your own typeface, and to load and save it in the Juce typeface format. Normally you should never need to deal directly with Typeface objects - the Font class does everything you typically need for rendering text. @see CustomTypeface, Font */ class JUCE_API Typeface : public ReferenceCountedObject { public: //============================================================================== /** A handy typedef for a pointer to a typeface. */ typedef ReferenceCountedObjectPtr Ptr; //============================================================================== /** Returns the font family of the typeface. @see Font::getTypefaceName */ const String& getName() const noexcept { return name; } //============================================================================== /** Returns the font style of the typeface. @see Font::getTypefaceStyle */ const String& getStyle() const noexcept { return style; } //============================================================================== /** Creates a new system typeface. */ static Ptr createSystemTypefaceFor (const Font& font); /** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image). The system will take its own internal copy of the data, so you can free the block once this method has returned. */ static Ptr createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize); //============================================================================== /** Destructor. */ virtual ~Typeface(); /** Returns true if this typeface can be used to render the specified font. When called, the font will already have been checked to make sure that its name and style flags match the typeface. */ virtual bool isSuitableForFont (const Font&) const { return true; } /** Returns the ascent of the font, as a proportion of its height. The height is considered to always be normalised as 1.0, so this will be a value less that 1.0, indicating the proportion of the font that lies above its baseline. */ virtual float getAscent() const = 0; /** Returns the descent of the font, as a proportion of its height. The height is considered to always be normalised as 1.0, so this will be a value less that 1.0, indicating the proportion of the font that lies below its baseline. */ virtual float getDescent() const = 0; /** Returns the value by which you should multiply a juce font-height value to convert it to the equivalent point-size. */ virtual float getHeightToPointsFactor() const = 0; /** Measures the width of a line of text. The distance returned is based on the font having an normalised height of 1.0. You should never need to call this directly! Use Font::getStringWidth() instead! */ virtual float getStringWidth (const String& text) = 0; /** Converts a line of text into its glyph numbers and their positions. The distances returned are based on the font having an normalised height of 1.0. You should never need to call this directly! Use Font::getGlyphPositions() instead! */ virtual void getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets) = 0; /** Returns the outline for a glyph. The path returned will be normalised to a font height of 1.0. */ virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; /** Returns a new EdgeTable that contains the path for the givem glyph, with the specified transform applied. */ virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight); /** Returns true if the typeface uses hinting. */ virtual bool isHinted() const { return false; } //============================================================================== /** Changes the number of fonts that are cached in memory. */ static void setTypefaceCacheSize (int numFontsToCache); /** Clears any fonts that are currently cached in memory. */ static void clearTypefaceCache(); /** On some platforms, this allows a specific path to be scanned. Currently only available when using FreeType. */ static void scanFolderForFonts (const File& folder); /** Makes an attempt at performing a good overall distortion that will scale a font of the given size to align vertically with the pixel grid. The path should be an unscaled (i.e. normalised to height of 1.0) path for a glyph. */ void applyVerticalHintingTransform (float fontHeight, Path& path); protected: //============================================================================== String name, style; Typeface (const String& name, const String& style) noexcept; static Ptr getFallbackTypeface(); private: struct HintingParams; friend struct ContainerDeletePolicy; ScopedPointer hintingParams; CriticalSection hintingLock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Typeface) }; #endif // JUCE_TYPEFACE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/000077500000000000000000000000001320201440200262455ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp000066400000000000000000000224531320201440200330510ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ AffineTransform::AffineTransform() noexcept : mat00 (1.0f), mat01 (0), mat02 (0), mat10 (0), mat11 (1.0f), mat12 (0) { } AffineTransform::AffineTransform (const AffineTransform& other) noexcept : mat00 (other.mat00), mat01 (other.mat01), mat02 (other.mat02), mat10 (other.mat10), mat11 (other.mat11), mat12 (other.mat12) { } AffineTransform::AffineTransform (const float m00, const float m01, const float m02, const float m10, const float m11, const float m12) noexcept : mat00 (m00), mat01 (m01), mat02 (m02), mat10 (m10), mat11 (m11), mat12 (m12) { } AffineTransform& AffineTransform::operator= (const AffineTransform& other) noexcept { mat00 = other.mat00; mat01 = other.mat01; mat02 = other.mat02; mat10 = other.mat10; mat11 = other.mat11; mat12 = other.mat12; return *this; } bool AffineTransform::operator== (const AffineTransform& other) const noexcept { return mat00 == other.mat00 && mat01 == other.mat01 && mat02 == other.mat02 && mat10 == other.mat10 && mat11 == other.mat11 && mat12 == other.mat12; } bool AffineTransform::operator!= (const AffineTransform& other) const noexcept { return ! operator== (other); } //============================================================================== bool AffineTransform::isIdentity() const noexcept { return (mat01 == 0) && (mat02 == 0) && (mat10 == 0) && (mat12 == 0) && (mat00 == 1.0f) && (mat11 == 1.0f); } const AffineTransform AffineTransform::identity; //============================================================================== AffineTransform AffineTransform::followedBy (const AffineTransform& other) const noexcept { return AffineTransform (other.mat00 * mat00 + other.mat01 * mat10, other.mat00 * mat01 + other.mat01 * mat11, other.mat00 * mat02 + other.mat01 * mat12 + other.mat02, other.mat10 * mat00 + other.mat11 * mat10, other.mat10 * mat01 + other.mat11 * mat11, other.mat10 * mat02 + other.mat11 * mat12 + other.mat12); } AffineTransform AffineTransform::translated (const float dx, const float dy) const noexcept { return AffineTransform (mat00, mat01, mat02 + dx, mat10, mat11, mat12 + dy); } AffineTransform AffineTransform::translation (const float dx, const float dy) noexcept { return AffineTransform (1.0f, 0, dx, 0, 1.0f, dy); } AffineTransform AffineTransform::withAbsoluteTranslation (const float tx, const float ty) const noexcept { return AffineTransform (mat00, mat01, tx, mat10, mat11, ty); } AffineTransform AffineTransform::rotated (const float rad) const noexcept { const float cosRad = std::cos (rad); const float sinRad = std::sin (rad); return AffineTransform (cosRad * mat00 + -sinRad * mat10, cosRad * mat01 + -sinRad * mat11, cosRad * mat02 + -sinRad * mat12, sinRad * mat00 + cosRad * mat10, sinRad * mat01 + cosRad * mat11, sinRad * mat02 + cosRad * mat12); } AffineTransform AffineTransform::rotation (const float rad) noexcept { const float cosRad = std::cos (rad); const float sinRad = std::sin (rad); return AffineTransform (cosRad, -sinRad, 0, sinRad, cosRad, 0); } AffineTransform AffineTransform::rotation (const float rad, const float pivotX, const float pivotY) noexcept { const float cosRad = std::cos (rad); const float sinRad = std::sin (rad); return AffineTransform (cosRad, -sinRad, -cosRad * pivotX + sinRad * pivotY + pivotX, sinRad, cosRad, -sinRad * pivotX + -cosRad * pivotY + pivotY); } AffineTransform AffineTransform::rotated (const float angle, const float pivotX, const float pivotY) const noexcept { return followedBy (rotation (angle, pivotX, pivotY)); } AffineTransform AffineTransform::scaled (const float factorX, const float factorY) const noexcept { return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02, factorY * mat10, factorY * mat11, factorY * mat12); } AffineTransform AffineTransform::scaled (const float factor) const noexcept { return AffineTransform (factor * mat00, factor * mat01, factor * mat02, factor * mat10, factor * mat11, factor * mat12); } AffineTransform AffineTransform::scale (const float factorX, const float factorY) noexcept { return AffineTransform (factorX, 0, 0, 0, factorY, 0); } AffineTransform AffineTransform::scale (const float factor) noexcept { return AffineTransform (factor, 0, 0, 0, factor, 0); } AffineTransform AffineTransform::scaled (const float factorX, const float factorY, const float pivotX, const float pivotY) const noexcept { return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX), factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY)); } AffineTransform AffineTransform::scale (const float factorX, const float factorY, const float pivotX, const float pivotY) noexcept { return AffineTransform (factorX, 0, pivotX * (1.0f - factorX), 0, factorY, pivotY * (1.0f - factorY)); } AffineTransform AffineTransform::shear (float shearX, float shearY) noexcept { return AffineTransform (1.0f, shearX, 0, shearY, 1.0f, 0); } AffineTransform AffineTransform::sheared (const float shearX, const float shearY) const noexcept { return AffineTransform (mat00 + shearX * mat10, mat01 + shearX * mat11, mat02 + shearX * mat12, mat10 + shearY * mat00, mat11 + shearY * mat01, mat12 + shearY * mat02); } AffineTransform AffineTransform::verticalFlip (const float height) noexcept { return AffineTransform (1.0f, 0, 0, 0, -1.0f, height); } AffineTransform AffineTransform::inverted() const noexcept { double determinant = (mat00 * mat11 - mat10 * mat01); if (determinant != 0.0) { determinant = 1.0 / determinant; const float dst00 = (float) ( mat11 * determinant); const float dst10 = (float) (-mat10 * determinant); const float dst01 = (float) (-mat01 * determinant); const float dst11 = (float) ( mat00 * determinant); return AffineTransform (dst00, dst01, -mat02 * dst00 - mat12 * dst01, dst10, dst11, -mat02 * dst10 - mat12 * dst11); } else { // singularity.. return *this; } } bool AffineTransform::isSingularity() const noexcept { return (mat00 * mat11 - mat10 * mat01) == 0; } AffineTransform AffineTransform::fromTargetPoints (const float x00, const float y00, const float x10, const float y10, const float x01, const float y01) noexcept { return AffineTransform (x10 - x00, x01 - x00, x00, y10 - y00, y01 - y00, y00); } AffineTransform AffineTransform::fromTargetPoints (const float sx1, const float sy1, const float tx1, const float ty1, const float sx2, const float sy2, const float tx2, const float ty2, const float sx3, const float sy3, const float tx3, const float ty3) noexcept { return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3) .inverted() .followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3)); } bool AffineTransform::isOnlyTranslation() const noexcept { return (mat01 == 0) && (mat10 == 0) && (mat00 == 1.0f) && (mat11 == 1.0f); } float AffineTransform::getScaleFactor() const noexcept { return (std::abs (mat00) + std::abs (mat11)) / 2.0f; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h000066400000000000000000000301201320201440200325040ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_AFFINETRANSFORM_H_INCLUDED #define JUCE_AFFINETRANSFORM_H_INCLUDED //============================================================================== /** Represents a 2D affine-transformation matrix. An affine transformation is a transformation such as a rotation, scale, shear, resize or translation. These are used for various 2D transformation tasks, e.g. with Path objects. @see Path, Point, Line */ class JUCE_API AffineTransform { public: //============================================================================== /** Creates an identity transform. */ AffineTransform() noexcept; /** Creates a copy of another transform. */ AffineTransform (const AffineTransform& other) noexcept; /** Creates a transform from a set of raw matrix values. The resulting matrix is: (mat00 mat01 mat02) (mat10 mat11 mat12) ( 0 0 1 ) */ AffineTransform (float mat00, float mat01, float mat02, float mat10, float mat11, float mat12) noexcept; /** Copies from another AffineTransform object */ AffineTransform& operator= (const AffineTransform& other) noexcept; /** Compares two transforms. */ bool operator== (const AffineTransform& other) const noexcept; /** Compares two transforms. */ bool operator!= (const AffineTransform& other) const noexcept; /** A ready-to-use identity transform, which you can use to append other transformations to. e.g. @code AffineTransform myTransform = AffineTransform::identity.rotated (.5f) .scaled (2.0f); @endcode */ static const AffineTransform identity; //============================================================================== /** Transforms a 2D coordinate using this matrix. */ template void transformPoint (ValueType& x, ValueType& y) const noexcept { const ValueType oldX = x; x = static_cast (mat00 * oldX + mat01 * y + mat02); y = static_cast (mat10 * oldX + mat11 * y + mat12); } /** Transforms two 2D coordinates using this matrix. This is just a shortcut for calling transformPoint() on each of these pairs of coordinates in turn. (And putting all the calculations into one function hopefully also gives the compiler a bit more scope for pipelining it). */ template void transformPoints (ValueType& x1, ValueType& y1, ValueType& x2, ValueType& y2) const noexcept { const ValueType oldX1 = x1, oldX2 = x2; x1 = static_cast (mat00 * oldX1 + mat01 * y1 + mat02); y1 = static_cast (mat10 * oldX1 + mat11 * y1 + mat12); x2 = static_cast (mat00 * oldX2 + mat01 * y2 + mat02); y2 = static_cast (mat10 * oldX2 + mat11 * y2 + mat12); } /** Transforms three 2D coordinates using this matrix. This is just a shortcut for calling transformPoint() on each of these pairs of coordinates in turn. (And putting all the calculations into one function hopefully also gives the compiler a bit more scope for pipelining it). */ template void transformPoints (ValueType& x1, ValueType& y1, ValueType& x2, ValueType& y2, ValueType& x3, ValueType& y3) const noexcept { const ValueType oldX1 = x1, oldX2 = x2, oldX3 = x3; x1 = static_cast (mat00 * oldX1 + mat01 * y1 + mat02); y1 = static_cast (mat10 * oldX1 + mat11 * y1 + mat12); x2 = static_cast (mat00 * oldX2 + mat01 * y2 + mat02); y2 = static_cast (mat10 * oldX2 + mat11 * y2 + mat12); x3 = static_cast (mat00 * oldX3 + mat01 * y3 + mat02); y3 = static_cast (mat10 * oldX3 + mat11 * y3 + mat12); } //============================================================================== /** Returns a new transform which is the same as this one followed by a translation. */ AffineTransform translated (float deltaX, float deltaY) const noexcept; /** Returns a new transform which is the same as this one followed by a translation. */ template AffineTransform translated (PointType delta) const noexcept { return translated ((float) delta.x, (float) delta.y); } /** Returns a new transform which is a translation. */ static AffineTransform translation (float deltaX, float deltaY) noexcept; /** Returns a new transform which is a translation. */ template static AffineTransform translation (PointType delta) noexcept { return translation ((float) delta.x, (float) delta.y); } /** Returns a copy of this transform with the specified translation matrix values. */ AffineTransform withAbsoluteTranslation (float translationX, float translationY) const noexcept; /** Returns a transform which is the same as this one followed by a rotation. The rotation is specified by a number of radians to rotate clockwise, centred around the origin (0, 0). */ AffineTransform rotated (float angleInRadians) const noexcept; /** Returns a transform which is the same as this one followed by a rotation about a given point. The rotation is specified by a number of radians to rotate clockwise, centred around the coordinates passed in. */ AffineTransform rotated (float angleInRadians, float pivotX, float pivotY) const noexcept; /** Returns a new transform which is a rotation about (0, 0). */ static AffineTransform rotation (float angleInRadians) noexcept; /** Returns a new transform which is a rotation about a given point. */ static AffineTransform rotation (float angleInRadians, float pivotX, float pivotY) noexcept; /** Returns a transform which is the same as this one followed by a re-scaling. The scaling is centred around the origin (0, 0). */ AffineTransform scaled (float factorX, float factorY) const noexcept; /** Returns a transform which is the same as this one followed by a re-scaling. The scaling is centred around the origin (0, 0). */ AffineTransform scaled (float factor) const noexcept; /** Returns a transform which is the same as this one followed by a re-scaling. The scaling is centred around the origin provided. */ AffineTransform scaled (float factorX, float factorY, float pivotX, float pivotY) const noexcept; /** Returns a new transform which is a re-scale about the origin. */ static AffineTransform scale (float factorX, float factorY) noexcept; /** Returns a new transform which is a re-scale about the origin. */ static AffineTransform scale (float factor) noexcept; /** Returns a new transform which is a re-scale centred around the point provided. */ static AffineTransform scale (float factorX, float factorY, float pivotX, float pivotY) noexcept; /** Returns a transform which is the same as this one followed by a shear. The shear is centred around the origin (0, 0). */ AffineTransform sheared (float shearX, float shearY) const noexcept; /** Returns a shear transform, centred around the origin (0, 0). */ static AffineTransform shear (float shearX, float shearY) noexcept; /** Returns a transform that will flip coordinates vertically within a window of the given height. This is handy for converting between upside-down coordinate systems such as OpenGL or CoreGraphics. */ static AffineTransform verticalFlip (float height) noexcept; /** Returns a matrix which is the inverse operation of this one. Some matrices don't have an inverse - in this case, the method will just return an identity transform. */ AffineTransform inverted() const noexcept; /** Returns the transform that will map three known points onto three coordinates that are supplied. This returns the transform that will transform (0, 0) into (x00, y00), (1, 0) to (x10, y10), and (0, 1) to (x01, y01). */ static AffineTransform fromTargetPoints (float x00, float y00, float x10, float y10, float x01, float y01) noexcept; /** Returns the transform that will map three specified points onto three target points. */ static AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1, float sourceX2, float sourceY2, float targetX2, float targetY2, float sourceX3, float sourceY3, float targetX3, float targetY3) noexcept; //============================================================================== /** Returns the result of concatenating another transformation after this one. */ AffineTransform followedBy (const AffineTransform& other) const noexcept; /** Returns true if this transform has no effect on points. */ bool isIdentity() const noexcept; /** Returns true if this transform maps to a singularity - i.e. if it has no inverse. */ bool isSingularity() const noexcept; /** Returns true if the transform only translates, and doesn't scale or rotate the points. */ bool isOnlyTranslation() const noexcept; /** If this transform is only a translation, this returns the X offset. @see isOnlyTranslation */ float getTranslationX() const noexcept { return mat02; } /** If this transform is only a translation, this returns the X offset. @see isOnlyTranslation */ float getTranslationY() const noexcept { return mat12; } /** Returns the approximate scale factor by which lengths will be transformed. Obviously a length may be scaled by entirely different amounts depending on its direction, so this is only appropriate as a rough guide. */ float getScaleFactor() const noexcept; //============================================================================== /* The transform matrix is: (mat00 mat01 mat02) (mat10 mat11 mat12) ( 0 0 1 ) */ float mat00, mat01, mat02; float mat10, mat11, mat12; private: //============================================================================== JUCE_LEAK_DETECTOR (AffineTransform) }; #endif // JUCE_AFFINETRANSFORM_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_BorderSize.h000066400000000000000000000135211320201440200314760ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_BORDERSIZE_H_INCLUDED #define JUCE_BORDERSIZE_H_INCLUDED //============================================================================== /** Specifies a set of gaps to be left around the sides of a rectangle. This is basically the size of the spaces at the top, bottom, left and right of a rectangle. It's used by various component classes to specify borders. @see Rectangle */ template class BorderSize { public: //============================================================================== /** Creates a null border. All sizes are left as 0. */ BorderSize() noexcept : top(), left(), bottom(), right() { } /** Creates a copy of another border. */ BorderSize (const BorderSize& other) noexcept : top (other.top), left (other.left), bottom (other.bottom), right (other.right) { } /** Creates a border with the given gaps. */ BorderSize (ValueType topGap, ValueType leftGap, ValueType bottomGap, ValueType rightGap) noexcept : top (topGap), left (leftGap), bottom (bottomGap), right (rightGap) { } /** Creates a border with the given gap on all sides. */ explicit BorderSize (ValueType allGaps) noexcept : top (allGaps), left (allGaps), bottom (allGaps), right (allGaps) { } //============================================================================== /** Returns the gap that should be left at the top of the region. */ ValueType getTop() const noexcept { return top; } /** Returns the gap that should be left at the top of the region. */ ValueType getLeft() const noexcept { return left; } /** Returns the gap that should be left at the top of the region. */ ValueType getBottom() const noexcept { return bottom; } /** Returns the gap that should be left at the top of the region. */ ValueType getRight() const noexcept { return right; } /** Returns the sum of the top and bottom gaps. */ ValueType getTopAndBottom() const noexcept { return top + bottom; } /** Returns the sum of the left and right gaps. */ ValueType getLeftAndRight() const noexcept { return left + right; } /** Returns true if this border has no thickness along any edge. */ bool isEmpty() const noexcept { return left + right + top + bottom == ValueType(); } //============================================================================== /** Changes the top gap. */ void setTop (ValueType newTopGap) noexcept { top = newTopGap; } /** Changes the left gap. */ void setLeft (ValueType newLeftGap) noexcept { left = newLeftGap; } /** Changes the bottom gap. */ void setBottom (ValueType newBottomGap) noexcept { bottom = newBottomGap; } /** Changes the right gap. */ void setRight (ValueType newRightGap) noexcept { right = newRightGap; } //============================================================================== /** Returns a rectangle with these borders removed from it. */ Rectangle subtractedFrom (const Rectangle& original) const noexcept { return Rectangle (original.getX() + left, original.getY() + top, original.getWidth() - (left + right), original.getHeight() - (top + bottom)); } /** Removes this border from a given rectangle. */ void subtractFrom (Rectangle& rectangle) const noexcept { rectangle = subtractedFrom (rectangle); } /** Returns a rectangle with these borders added around it. */ Rectangle addedTo (const Rectangle& original) const noexcept { return Rectangle (original.getX() - left, original.getY() - top, original.getWidth() + (left + right), original.getHeight() + (top + bottom)); } /** Adds this border around a given rectangle. */ void addTo (Rectangle& rectangle) const noexcept { rectangle = addedTo (rectangle); } //============================================================================== bool operator== (const BorderSize& other) const noexcept { return top == other.top && left == other.left && bottom == other.bottom && right == other.right; } bool operator!= (const BorderSize& other) const noexcept { return ! operator== (other); } private: //============================================================================== ValueType top, left, bottom, right; }; #endif // JUCE_BORDERSIZE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp000066400000000000000000000552321320201440200316020ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ const int juce_edgeTableDefaultEdgesPerLine = 32; //============================================================================== EdgeTable::EdgeTable (const Rectangle& area, const Path& path, const AffineTransform& transform) : bounds (area), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), needToCheckEmptiness (true) { allocate(); int* t = table; for (int i = bounds.getHeight(); --i >= 0;) { *t = 0; t += lineStrideElements; } const int leftLimit = bounds.getX() << 8; const int topLimit = bounds.getY() << 8; const int rightLimit = bounds.getRight() << 8; const int heightLimit = bounds.getHeight() << 8; PathFlatteningIterator iter (path, transform); while (iter.next()) { int y1 = roundToInt (iter.y1 * 256.0f); int y2 = roundToInt (iter.y2 * 256.0f); if (y1 != y2) { y1 -= topLimit; y2 -= topLimit; const int startY = y1; int direction = -1; if (y1 > y2) { std::swap (y1, y2); direction = 1; } if (y1 < 0) y1 = 0; if (y2 > heightLimit) y2 = heightLimit; if (y1 < y2) { const double startX = 256.0f * iter.x1; const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1); const int stepSize = jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier))); do { const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255)); int x = roundToInt (startX + multiplier * ((y1 + (step >> 1)) - startY)); if (x < leftLimit) x = leftLimit; else if (x >= rightLimit) x = rightLimit - 1; addEdgePoint (x, y1 >> 8, direction * step); y1 += step; } while (y1 < y2); } } } sanitiseLevels (path.isUsingNonZeroWinding()); } EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) : bounds (rectangleToAdd), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), needToCheckEmptiness (true) { allocate(); table[0] = 0; const int x1 = rectangleToAdd.getX() << 8; const int x2 = rectangleToAdd.getRight() << 8; int* t = table; for (int i = rectangleToAdd.getHeight(); --i >= 0;) { t[0] = 2; t[1] = x1; t[2] = 255; t[3] = x2; t[4] = 0; t += lineStrideElements; } } EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) : bounds (rectanglesToAdd.getBounds()), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), needToCheckEmptiness (true) { allocate(); clearLineSizes(); for (const Rectangle* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r) { const int x1 = r->getX() << 8; const int x2 = r->getRight() << 8; int y = r->getY() - bounds.getY(); for (int j = r->getHeight(); --j >= 0;) addEdgePointPair (x1, x2, y++, 255); } sanitiseLevels (true); } EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) : bounds (rectanglesToAdd.getBounds().getSmallestIntegerContainer()), maxEdgesPerLine (rectanglesToAdd.getNumRectangles() * 2), lineStrideElements (rectanglesToAdd.getNumRectangles() * 4 + 1), needToCheckEmptiness (true) { bounds.setHeight (bounds.getHeight() + 1); allocate(); clearLineSizes(); for (const Rectangle* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r) { const int x1 = roundToInt (r->getX() * 256.0f); const int x2 = roundToInt (r->getRight() * 256.0f); const int y1 = roundToInt (r->getY() * 256.0f) - (bounds.getY() << 8); const int y2 = roundToInt (r->getBottom() * 256.0f) - (bounds.getY() << 8); if (x2 <= x1 || y2 <= y1) continue; int y = y1 >> 8; const int lastLine = y2 >> 8; if (y == lastLine) { addEdgePointPair (x1, x2, y, y2 - y1); } else { addEdgePointPair (x1, x2, y++, 255 - (y1 & 255)); while (y < lastLine) addEdgePointPair (x1, x2, y++, 255); jassert (y < bounds.getHeight()); addEdgePointPair (x1, x2, y, y2 & 255); } } sanitiseLevels (true); } EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) : bounds (Rectangle ((int) std::floor (rectangleToAdd.getX()), roundToInt (rectangleToAdd.getY() * 256.0f) >> 8, 2 + (int) rectangleToAdd.getWidth(), 2 + (int) rectangleToAdd.getHeight())), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), needToCheckEmptiness (true) { jassert (! rectangleToAdd.isEmpty()); allocate(); table[0] = 0; const int x1 = roundToInt (rectangleToAdd.getX() * 256.0f); const int x2 = roundToInt (rectangleToAdd.getRight() * 256.0f); int y1 = roundToInt (rectangleToAdd.getY() * 256.0f) - (bounds.getY() << 8); jassert (y1 < 256); int y2 = roundToInt (rectangleToAdd.getBottom() * 256.0f) - (bounds.getY() << 8); if (x2 <= x1 || y2 <= y1) { bounds.setHeight (0); return; } int lineY = 0; int* t = table; if ((y1 >> 8) == (y2 >> 8)) { t[0] = 2; t[1] = x1; t[2] = y2 - y1; t[3] = x2; t[4] = 0; ++lineY; t += lineStrideElements; } else { t[0] = 2; t[1] = x1; t[2] = 255 - (y1 & 255); t[3] = x2; t[4] = 0; ++lineY; t += lineStrideElements; while (lineY < (y2 >> 8)) { t[0] = 2; t[1] = x1; t[2] = 255; t[3] = x2; t[4] = 0; ++lineY; t += lineStrideElements; } jassert (lineY < bounds.getHeight()); t[0] = 2; t[1] = x1; t[2] = y2 & 255; t[3] = x2; t[4] = 0; ++lineY; t += lineStrideElements; } while (lineY < bounds.getHeight()) { t[0] = 0; t += lineStrideElements; ++lineY; } } EdgeTable::EdgeTable (const EdgeTable& other) { operator= (other); } EdgeTable& EdgeTable::operator= (const EdgeTable& other) { bounds = other.bounds; maxEdgesPerLine = other.maxEdgesPerLine; lineStrideElements = other.lineStrideElements; needToCheckEmptiness = other.needToCheckEmptiness; allocate(); copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight()); return *this; } EdgeTable::~EdgeTable() { } //============================================================================== static size_t getEdgeTableAllocationSize (int lineStride, int height) noexcept { // (leave an extra line at the end for use as scratch space) return (size_t) (lineStride * (2 + jmax (0, height))); } void EdgeTable::allocate() { table.malloc (getEdgeTableAllocationSize (lineStrideElements, bounds.getHeight())); } void EdgeTable::clearLineSizes() noexcept { int* t = table; for (int i = bounds.getHeight(); --i >= 0;) { *t = 0; t += lineStrideElements; } } void EdgeTable::copyEdgeTableData (int* dest, const int destLineStride, const int* src, const int srcLineStride, int numLines) noexcept { while (--numLines >= 0) { memcpy (dest, src, (size_t) (src[0] * 2 + 1) * sizeof (int)); src += srcLineStride; dest += destLineStride; } } void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept { // Convert the table from relative windings to absolute levels.. int* lineStart = table; for (int y = bounds.getHeight(); --y >= 0;) { int num = lineStart[0]; if (num > 0) { LineItem* items = reinterpret_cast (lineStart + 1); LineItem* const itemsEnd = items + num; // sort the X coords std::sort (items, itemsEnd); const LineItem* src = items; int correctedNum = num; int level = 0; while (src < itemsEnd) { level += src->level; const int x = src->x; ++src; while (src < itemsEnd && src->x == x) { level += src->level; ++src; --correctedNum; } int corrected = std::abs (level); if (corrected >> 8) { if (useNonZeroWinding) { corrected = 255; } else { corrected &= 511; if (corrected >> 8) corrected = 511 - corrected; } } items->x = x; items->level = corrected; ++items; } lineStart[0] = correctedNum; (items - 1)->level = 0; // force the last level to 0, just in case something went wrong in creating the table } lineStart += lineStrideElements; } } void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) { if (newNumEdgesPerLine != maxEdgesPerLine) { maxEdgesPerLine = newNumEdgesPerLine; jassert (bounds.getHeight() > 0); const int newLineStrideElements = maxEdgesPerLine * 2 + 1; HeapBlock newTable (getEdgeTableAllocationSize (newLineStrideElements, bounds.getHeight())); copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight()); table.swapWith (newTable); lineStrideElements = newLineStrideElements; } } void EdgeTable::optimiseTable() { int maxLineElements = 0; for (int i = bounds.getHeight(); --i >= 0;) maxLineElements = jmax (maxLineElements, table [i * lineStrideElements]); remapTableForNumEdges (maxLineElements); } void EdgeTable::addEdgePoint (const int x, const int y, const int winding) { jassert (y >= 0 && y < bounds.getHeight()); int* line = table + lineStrideElements * y; const int numPoints = line[0]; if (numPoints >= maxEdgesPerLine) { remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); jassert (numPoints < maxEdgesPerLine); line = table + lineStrideElements * y; } line[0]++; int n = numPoints << 1; line [n + 1] = x; line [n + 2] = winding; } void EdgeTable::addEdgePointPair (int x1, int x2, int y, int winding) { jassert (y >= 0 && y < bounds.getHeight()); int* line = table + lineStrideElements * y; const int numPoints = line[0]; if (numPoints + 1 >= maxEdgesPerLine) { remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); jassert (numPoints < maxEdgesPerLine); line = table + lineStrideElements * y; } line[0] = numPoints + 2; line += numPoints << 1; line[1] = x1; line[2] = winding; line[3] = x2; line[4] = -winding; } void EdgeTable::translate (float dx, const int dy) noexcept { bounds.translate ((int) std::floor (dx), dy); int* lineStart = table; const int intDx = (int) (dx * 256.0f); for (int i = bounds.getHeight(); --i >= 0;) { int* line = lineStart; lineStart += lineStrideElements; int num = *line++; while (--num >= 0) { *line += intDx; line += 2; } } } void EdgeTable::multiplyLevels (float amount) { int* lineStart = table; const int multiplier = (int) (amount * 256.0f); for (int y = 0; y < bounds.getHeight(); ++y) { int numPoints = lineStart[0]; LineItem* item = reinterpret_cast (lineStart + 1); lineStart += lineStrideElements; while (--numPoints > 0) { item->level = jmin (255, (item->level * multiplier) >> 8); ++item; } } } void EdgeTable::intersectWithEdgeTableLine (const int y, const int* const otherLine) { jassert (y >= 0 && y < bounds.getHeight()); int* srcLine = table + lineStrideElements * y; int srcNum1 = *srcLine; if (srcNum1 == 0) return; int srcNum2 = *otherLine; if (srcNum2 == 0) { *srcLine = 0; return; } const int right = bounds.getRight() << 8; // optimise for the common case where our line lies entirely within a // single pair of points, as happens when clipping to a simple rect. if (srcNum2 == 2 && otherLine[2] >= 255) { clipEdgeTableLineToRange (srcLine, otherLine[1], jmin (right, otherLine[3])); return; } bool isUsingTempSpace = false; const int* src1 = srcLine + 1; int x1 = *src1++; const int* src2 = otherLine + 1; int x2 = *src2++; int destIndex = 0, destTotal = 0; int level1 = 0, level2 = 0; int lastX = std::numeric_limits::min(), lastLevel = 0; while (srcNum1 > 0 && srcNum2 > 0) { int nextX; if (x1 <= x2) { if (x1 == x2) { level2 = *src2++; x2 = *src2++; --srcNum2; } nextX = x1; level1 = *src1++; x1 = *src1++; --srcNum1; } else { nextX = x2; level2 = *src2++; x2 = *src2++; --srcNum2; } if (nextX > lastX) { if (nextX >= right) break; lastX = nextX; const int nextLevel = (level1 * (level2 + 1)) >> 8; jassert (isPositiveAndBelow (nextLevel, (int) 256)); if (nextLevel != lastLevel) { if (destTotal >= maxEdgesPerLine) { srcLine[0] = destTotal; if (isUsingTempSpace) { const size_t tempSize = (size_t) srcNum1 * 2 * sizeof (int); int* const oldTemp = static_cast (alloca (tempSize)); memcpy (oldTemp, src1, tempSize); remapTableForNumEdges (jmax (256, destTotal * 2)); srcLine = table + lineStrideElements * y; int* const newTemp = table + lineStrideElements * bounds.getHeight(); memcpy (newTemp, oldTemp, tempSize); src1 = newTemp; } else { remapTableForNumEdges (jmax (256, destTotal * 2)); srcLine = table + lineStrideElements * y; } } ++destTotal; lastLevel = nextLevel; if (! isUsingTempSpace) { isUsingTempSpace = true; int* const temp = table + lineStrideElements * bounds.getHeight(); memcpy (temp, src1, (size_t) srcNum1 * 2 * sizeof (int)); src1 = temp; } srcLine[++destIndex] = nextX; srcLine[++destIndex] = nextLevel; } } } if (lastLevel > 0) { if (destTotal >= maxEdgesPerLine) { srcLine[0] = destTotal; remapTableForNumEdges (jmax (256, destTotal * 2)); srcLine = table + lineStrideElements * y; } ++destTotal; srcLine[++destIndex] = right; srcLine[++destIndex] = 0; } srcLine[0] = destTotal; } void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) noexcept { int* lastItem = dest + (dest[0] * 2 - 1); if (x2 < lastItem[0]) { if (x2 <= dest[1]) { dest[0] = 0; return; } while (x2 < lastItem[-2]) { --(dest[0]); lastItem -= 2; } lastItem[0] = x2; lastItem[1] = 0; } if (x1 > dest[1]) { while (lastItem[0] > x1) lastItem -= 2; const int itemsRemoved = (int) (lastItem - (dest + 1)) / 2; if (itemsRemoved > 0) { dest[0] -= itemsRemoved; memmove (dest + 1, lastItem, (size_t) dest[0] * (sizeof (int) * 2)); } dest[1] = x1; } } //============================================================================== void EdgeTable::clipToRectangle (const Rectangle& r) { const Rectangle clipped (r.getIntersection (bounds)); if (clipped.isEmpty()) { needToCheckEmptiness = false; bounds.setHeight (0); } else { const int top = clipped.getY() - bounds.getY(); const int bottom = clipped.getBottom() - bounds.getY(); if (bottom < bounds.getHeight()) bounds.setHeight (bottom); for (int i = top; --i >= 0;) table [lineStrideElements * i] = 0; if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight()) { const int x1 = clipped.getX() << 8; const int x2 = jmin (bounds.getRight(), clipped.getRight()) << 8; int* line = table + lineStrideElements * top; for (int i = bottom - top; --i >= 0;) { if (line[0] != 0) clipEdgeTableLineToRange (line, x1, x2); line += lineStrideElements; } } needToCheckEmptiness = true; } } void EdgeTable::excludeRectangle (const Rectangle& r) { const Rectangle clipped (r.getIntersection (bounds)); if (! clipped.isEmpty()) { const int top = clipped.getY() - bounds.getY(); const int bottom = clipped.getBottom() - bounds.getY(); const int rectLine[] = { 4, std::numeric_limits::min(), 255, clipped.getX() << 8, 0, clipped.getRight() << 8, 255, std::numeric_limits::max(), 0 }; for (int i = top; i < bottom; ++i) intersectWithEdgeTableLine (i, rectLine); needToCheckEmptiness = true; } } void EdgeTable::clipToEdgeTable (const EdgeTable& other) { const Rectangle clipped (other.bounds.getIntersection (bounds)); if (clipped.isEmpty()) { needToCheckEmptiness = false; bounds.setHeight (0); } else { const int top = clipped.getY() - bounds.getY(); const int bottom = clipped.getBottom() - bounds.getY(); if (bottom < bounds.getHeight()) bounds.setHeight (bottom); if (clipped.getRight() < bounds.getRight()) bounds.setRight (clipped.getRight()); for (int i = 0; i < top; ++i) table [lineStrideElements * i] = 0; const int* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY()); for (int i = top; i < bottom; ++i) { intersectWithEdgeTableLine (i, otherLine); otherLine += other.lineStrideElements; } needToCheckEmptiness = true; } } void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) { y -= bounds.getY(); if (y < 0 || y >= bounds.getHeight()) return; needToCheckEmptiness = true; if (numPixels <= 0) { table [lineStrideElements * y] = 0; return; } int* tempLine = static_cast (alloca ((size_t) (numPixels * 2 + 4) * sizeof (int))); int destIndex = 0, lastLevel = 0; while (--numPixels >= 0) { const int alpha = *mask; mask += maskStride; if (alpha != lastLevel) { tempLine[++destIndex] = (x << 8); tempLine[++destIndex] = alpha; lastLevel = alpha; } ++x; } if (lastLevel > 0) { tempLine[++destIndex] = (x << 8); tempLine[++destIndex] = 0; } tempLine[0] = destIndex >> 1; intersectWithEdgeTableLine (y, tempLine); } bool EdgeTable::isEmpty() noexcept { if (needToCheckEmptiness) { needToCheckEmptiness = false; int* t = table; for (int i = bounds.getHeight(); --i >= 0;) { if (t[0] > 1) return false; t += lineStrideElements; } bounds.setHeight (0); } return bounds.getHeight() == 0; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h000066400000000000000000000215531320201440200312460ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_EDGETABLE_H_INCLUDED #define JUCE_EDGETABLE_H_INCLUDED //============================================================================== /** A table of horizontal scan-line segments - used for rasterising Paths. @see Path, Graphics */ class JUCE_API EdgeTable { public: //============================================================================== /** Creates an edge table containing a path. A table is created with a fixed vertical range, and only sections of the path which lie within this range will be added to the table. @param clipLimits only the region of the path that lies within this area will be added @param pathToAdd the path to add to the table @param transform a transform to apply to the path being added */ EdgeTable (const Rectangle& clipLimits, const Path& pathToAdd, const AffineTransform& transform); /** Creates an edge table containing a rectangle. */ explicit EdgeTable (const Rectangle& rectangleToAdd); /** Creates an edge table containing a rectangle list. */ explicit EdgeTable (const RectangleList& rectanglesToAdd); /** Creates an edge table containing a rectangle list. */ explicit EdgeTable (const RectangleList& rectanglesToAdd); /** Creates an edge table containing a rectangle. */ explicit EdgeTable (const Rectangle& rectangleToAdd); /** Creates a copy of another edge table. */ EdgeTable (const EdgeTable&); /** Copies from another edge table. */ EdgeTable& operator= (const EdgeTable&); /** Destructor. */ ~EdgeTable(); //============================================================================== void clipToRectangle (const Rectangle& r); void excludeRectangle (const Rectangle& r); void clipToEdgeTable (const EdgeTable&); void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels); bool isEmpty() noexcept; const Rectangle& getMaximumBounds() const noexcept { return bounds; } void translate (float dx, int dy) noexcept; /** Scales all the alpha-levels in the table by the given multiplier. */ void multiplyLevels (float factor); /** Reduces the amount of space the table has allocated. This will shrink the table down to use as little memory as possible - useful for read-only tables that get stored and re-used for rendering. */ void optimiseTable(); //============================================================================== /** Iterates the lines in the table, for rendering. This function will iterate each line in the table, and call a user-defined class to render each pixel or continuous line of pixels that the table contains. @param iterationCallback this templated class must contain the following methods: @code inline void setEdgeTableYPos (int y); inline void handleEdgeTablePixel (int x, int alphaLevel) const; inline void handleEdgeTablePixelFull (int x) const; inline void handleEdgeTableLine (int x, int width, int alphaLevel) const; inline void handleEdgeTableLineFull (int x, int width) const; @endcode (these don't necessarily have to be 'const', but it might help it go faster) */ template void iterate (EdgeTableIterationCallback& iterationCallback) const noexcept { const int* lineStart = table; for (int y = 0; y < bounds.getHeight(); ++y) { const int* line = lineStart; lineStart += lineStrideElements; int numPoints = line[0]; if (--numPoints > 0) { int x = *++line; jassert ((x >> 8) >= bounds.getX() && (x >> 8) < bounds.getRight()); int levelAccumulator = 0; iterationCallback.setEdgeTableYPos (bounds.getY() + y); while (--numPoints >= 0) { const int level = *++line; jassert (isPositiveAndBelow (level, (int) 256)); const int endX = *++line; jassert (endX >= x); const int endOfRun = (endX >> 8); if (endOfRun == (x >> 8)) { // small segment within the same pixel, so just save it for the next // time round.. levelAccumulator += (endX - x) * level; } else { // plot the fist pixel of this segment, including any accumulated // levels from smaller segments that haven't been drawn yet levelAccumulator += (0x100 - (x & 0xff)) * level; levelAccumulator >>= 8; x >>= 8; if (levelAccumulator > 0) { if (levelAccumulator >= 255) iterationCallback.handleEdgeTablePixelFull (x); else iterationCallback.handleEdgeTablePixel (x, levelAccumulator); } // if there's a run of similar pixels, do it all in one go.. if (level > 0) { jassert (endOfRun <= bounds.getRight()); const int numPix = endOfRun - ++x; if (numPix > 0) iterationCallback.handleEdgeTableLine (x, numPix, level); } // save the bit at the end to be drawn next time round the loop. levelAccumulator = (endX & 0xff) * level; } x = endX; } levelAccumulator >>= 8; if (levelAccumulator > 0) { x >>= 8; jassert (x >= bounds.getX() && x < bounds.getRight()); if (levelAccumulator >= 255) iterationCallback.handleEdgeTablePixelFull (x); else iterationCallback.handleEdgeTablePixel (x, levelAccumulator); } } } } private: //============================================================================== // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc struct LineItem { int x, level; bool operator< (const LineItem& other) const noexcept { return x < other.x; } }; HeapBlock table; Rectangle bounds; int maxEdgesPerLine, lineStrideElements; bool needToCheckEmptiness; void allocate(); void clearLineSizes() noexcept; void addEdgePoint (int x, int y, int winding); void addEdgePointPair (int x1, int x2, int y, int winding); void remapTableForNumEdges (int newNumEdgesPerLine); void intersectWithEdgeTableLine (int y, const int* otherLine); void clipEdgeTableLineToRange (int* line, int x1, int x2) noexcept; void sanitiseLevels (bool useNonZeroWinding) noexcept; static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) noexcept; JUCE_LEAK_DETECTOR (EdgeTable) }; #endif // JUCE_EDGETABLE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h000066400000000000000000000431101320201440200303120ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_LINE_H_INCLUDED #define JUCE_LINE_H_INCLUDED //============================================================================== /** Represents a line. This class contains a bunch of useful methods for various geometric tasks. The ValueType template parameter should be a primitive type - float or double are what it's designed for. Integer types will work in a basic way, but some methods that perform mathematical operations may not compile, or they may not produce sensible results. @see Point, Rectangle, Path, Graphics::drawLine */ template class Line { public: //============================================================================== /** Creates a line, using (0, 0) as its start and end points. */ Line() noexcept {} /** Creates a copy of another line. */ Line (const Line& other) noexcept : start (other.start), end (other.end) { } /** Creates a line based on the coordinates of its start and end points. */ Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) noexcept : start (startX, startY), end (endX, endY) { } /** Creates a line from its start and end points. */ Line (const Point startPoint, const Point endPoint) noexcept : start (startPoint), end (endPoint) { } /** Copies a line from another one. */ Line& operator= (const Line& other) noexcept { start = other.start; end = other.end; return *this; } /** Destructor. */ ~Line() noexcept {} //============================================================================== /** Returns the x coordinate of the line's start point. */ inline ValueType getStartX() const noexcept { return start.x; } /** Returns the y coordinate of the line's start point. */ inline ValueType getStartY() const noexcept { return start.y; } /** Returns the x coordinate of the line's end point. */ inline ValueType getEndX() const noexcept { return end.x; } /** Returns the y coordinate of the line's end point. */ inline ValueType getEndY() const noexcept { return end.y; } /** Returns the line's start point. */ inline Point getStart() const noexcept { return start; } /** Returns the line's end point. */ inline Point getEnd() const noexcept { return end; } /** Changes this line's start point */ void setStart (ValueType newStartX, ValueType newStartY) noexcept { start.setXY (newStartX, newStartY); } /** Changes this line's end point */ void setEnd (ValueType newEndX, ValueType newEndY) noexcept { end.setXY (newEndX, newEndY); } /** Changes this line's start point */ void setStart (const Point newStart) noexcept { start = newStart; } /** Changes this line's end point */ void setEnd (const Point newEnd) noexcept { end = newEnd; } /** Returns a line that is the same as this one, but with the start and end reversed, */ const Line reversed() const noexcept { return Line (end, start); } /** Applies an affine transform to the line's start and end points. */ void applyTransform (const AffineTransform& transform) noexcept { start.applyTransform (transform); end.applyTransform (transform); } //============================================================================== /** Returns the length of the line. */ ValueType getLength() const noexcept { return start.getDistanceFrom (end); } /** Returns true if the line's start and end x coordinates are the same. */ bool isVertical() const noexcept { return start.x == end.x; } /** Returns true if the line's start and end y coordinates are the same. */ bool isHorizontal() const noexcept { return start.y == end.y; } /** Returns the line's angle. This value is the number of radians clockwise from the 12 o'clock direction, where the line's start point is considered to be at the centre. */ typename Point::FloatType getAngle() const noexcept { return start.getAngleToPoint (end); } /** Casts this line to float coordinates. */ Line toFloat() const noexcept { return Line (start.toFloat(), end.toFloat()); } /** Casts this line to double coordinates. */ Line toDouble() const noexcept { return Line (start.toDouble(), end.toDouble()); } //============================================================================== /** Compares two lines. */ bool operator== (const Line& other) const noexcept { return start == other.start && end == other.end; } /** Compares two lines. */ bool operator!= (const Line& other) const noexcept { return start != other.start || end != other.end; } //============================================================================== /** Finds the intersection between two lines. @param line the line to intersect with @returns the point at which the lines intersect, even if this lies beyond the end of the lines */ Point getIntersection (const Line& line) const noexcept { Point p; findIntersection (start, end, line.start, line.end, p); return p; } /** Finds the intersection between two lines. @param line the other line @param intersection the position of the point where the lines meet (or where they would meet if they were infinitely long) the intersection (if the lines intersect). If the lines are parallel, this will just be set to the position of one of the line's endpoints. @returns true if the line segments intersect; false if they dont. Even if they don't intersect, the intersection coordinates returned will still be valid */ bool intersects (const Line& line, Point& intersection) const noexcept { return findIntersection (start, end, line.start, line.end, intersection); } /** Returns true if this line intersects another. */ bool intersects (const Line& other) const noexcept { Point ignored; return findIntersection (start, end, other.start, other.end, ignored); } //============================================================================== /** Returns the location of the point which is a given distance along this line. @param distanceFromStart the distance to move along the line from its start point. This value can be negative or longer than the line itself @see getPointAlongLineProportionally */ Point getPointAlongLine (ValueType distanceFromStart) const noexcept { return start + (end - start) * (distanceFromStart / getLength()); } /** Returns a point which is a certain distance along and to the side of this line. This effectively moves a given distance along the line, then another distance perpendicularly to this, and returns the resulting position. @param distanceFromStart the distance to move along the line from its start point. This value can be negative or longer than the line itself @param perpendicularDistance how far to move sideways from the line. If you're looking along the line from its start towards its end, then a positive value here will move to the right, negative value move to the left. */ Point getPointAlongLine (ValueType distanceFromStart, ValueType perpendicularDistance) const noexcept { const Point delta (end - start); const double length = juce_hypot ((double) delta.x, (double) delta.y); if (length <= 0) return start; return Point (start.x + static_cast ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length), start.y + static_cast ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length)); } /** Returns the location of the point which is a given distance along this line proportional to the line's length. @param proportionOfLength the distance to move along the line from its start point, in multiples of the line's length. So a value of 0.0 will return the line's start point and a value of 1.0 will return its end point. (This value can be negative or greater than 1.0). @see getPointAlongLine */ Point getPointAlongLineProportionally (ValueType proportionOfLength) const noexcept { return start + (end - start) * proportionOfLength; } /** Returns the smallest distance between this line segment and a given point. So if the point is close to the line, this will return the perpendicular distance from the line; if the point is a long way beyond one of the line's end-point's, it'll return the straight-line distance to the nearest end-point. pointOnLine receives the position of the point that is found. @returns the point's distance from the line @see getPositionAlongLineOfNearestPoint */ ValueType getDistanceFromPoint (const Point targetPoint, Point& pointOnLine) const noexcept { const Point delta (end - start); const double length = delta.x * delta.x + delta.y * delta.y; if (length > 0) { const double prop = ((targetPoint.x - start.x) * delta.x + (targetPoint.y - start.y) * delta.y) / length; if (prop >= 0 && prop <= 1.0) { pointOnLine = start + delta * static_cast (prop); return targetPoint.getDistanceFrom (pointOnLine); } } const float fromStart = targetPoint.getDistanceFrom (start); const float fromEnd = targetPoint.getDistanceFrom (end); if (fromStart < fromEnd) { pointOnLine = start; return fromStart; } else { pointOnLine = end; return fromEnd; } } /** Finds the point on this line which is nearest to a given point, and returns its position as a proportional position along the line. @returns a value 0 to 1.0 which is the distance along this line from the line's start to the point which is nearest to the point passed-in. To turn this number into a position, use getPointAlongLineProportionally(). @see getDistanceFromPoint, getPointAlongLineProportionally */ ValueType findNearestProportionalPositionTo (const Point point) const noexcept { const Point delta (end - start); const double length = delta.x * delta.x + delta.y * delta.y; return length <= 0 ? 0 : jlimit (ValueType(), static_cast (1), static_cast ((((point.x - start.x) * delta.x + (point.y - start.y) * delta.y) / length))); } /** Finds the point on this line which is nearest to a given point. @see getDistanceFromPoint, findNearestProportionalPositionTo */ Point findNearestPointTo (const Point point) const noexcept { return getPointAlongLineProportionally (findNearestProportionalPositionTo (point)); } /** Returns true if the given point lies above this line. The return value is true if the point's y coordinate is less than the y coordinate of this line at the given x (assuming the line extends infinitely in both directions). */ bool isPointAbove (const Point point) const noexcept { return start.x != end.x && point.y < ((end.y - start.y) * (point.x - start.x)) / (end.x - start.x) + start.y; } //============================================================================== /** Returns a shortened copy of this line. This will chop off part of the start of this line by a certain amount, (leaving the end-point the same), and return the new line. */ Line withShortenedStart (ValueType distanceToShortenBy) const noexcept { return Line (getPointAlongLine (jmin (distanceToShortenBy, getLength())), end); } /** Returns a shortened copy of this line. This will chop off part of the end of this line by a certain amount, (leaving the start-point the same), and return the new line. */ Line withShortenedEnd (ValueType distanceToShortenBy) const noexcept { const ValueType length = getLength(); return Line (start, getPointAlongLine (length - jmin (distanceToShortenBy, length))); } private: //============================================================================== Point start, end; static bool findIntersection (const Point p1, const Point p2, const Point p3, const Point p4, Point& intersection) noexcept { if (p2 == p3) { intersection = p2; return true; } const Point d1 (p2 - p1); const Point d2 (p4 - p3); const ValueType divisor = d1.x * d2.y - d2.x * d1.y; if (divisor == 0) { if (! (d1.isOrigin() || d2.isOrigin())) { if (d1.y == 0 && d2.y != 0) { const ValueType along = (p1.y - p3.y) / d2.y; intersection = p1.withX (p3.x + along * d2.x); return along >= 0 && along <= static_cast (1); } else if (d2.y == 0 && d1.y != 0) { const ValueType along = (p3.y - p1.y) / d1.y; intersection = p3.withX (p1.x + along * d1.x); return along >= 0 && along <= static_cast (1); } else if (d1.x == 0 && d2.x != 0) { const ValueType along = (p1.x - p3.x) / d2.x; intersection = p1.withY (p3.y + along * d2.y); return along >= 0 && along <= static_cast (1); } else if (d2.x == 0 && d1.x != 0) { const ValueType along = (p3.x - p1.x) / d1.x; intersection = p3.withY (p1.y + along * d1.y); return along >= 0 && along <= static_cast (1); } } intersection = (p2 + p3) / static_cast (2); return false; } const ValueType along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor; intersection = p1 + d1 * along1; if (along1 < 0 || along1 > static_cast (1)) return false; const ValueType along2 = ((p1.y - p3.y) * d1.x - (p1.x - p3.x) * d1.y) / divisor; return along2 >= 0 && along2 <= static_cast (1); } }; #endif // JUCE_LINE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp000066400000000000000000001411771320201440200306660ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ // tests that some coordinates aren't NaNs #define JUCE_CHECK_COORDS_ARE_VALID(x, y) \ jassert (x == x && y == y); //============================================================================== namespace PathHelpers { const float ellipseAngularIncrement = 0.05f; static String nextToken (String::CharPointerType& t) { t = t.findEndOfWhitespace(); String::CharPointerType start (t); size_t numChars = 0; while (! (t.isEmpty() || t.isWhitespace())) { ++t; ++numChars; } return String (start, numChars); } inline double lengthOf (float x1, float y1, float x2, float y2) noexcept { return juce_hypot ((double) (x1 - x2), (double) (y1 - y2)); } } //============================================================================== const float Path::lineMarker = 100001.0f; const float Path::moveMarker = 100002.0f; const float Path::quadMarker = 100003.0f; const float Path::cubicMarker = 100004.0f; const float Path::closeSubPathMarker = 100005.0f; //============================================================================== Path::PathBounds::PathBounds() noexcept : pathXMin (0), pathXMax (0), pathYMin (0), pathYMax (0) { } Rectangle Path::PathBounds::getRectangle() const noexcept { return Rectangle (pathXMin, pathYMin, pathXMax - pathXMin, pathYMax - pathYMin); } void Path::PathBounds::reset() noexcept { pathXMin = pathYMin = pathYMax = pathXMax = 0; } void Path::PathBounds::reset (const float x, const float y) noexcept { pathXMin = pathXMax = x; pathYMin = pathYMax = y; } void Path::PathBounds::extend (const float x, const float y) noexcept { pathXMin = jmin (pathXMin, x); pathXMax = jmax (pathXMax, x); pathYMin = jmin (pathYMin, y); pathYMax = jmax (pathYMax, y); } void Path::PathBounds::extend (const float x1, const float y1, const float x2, const float y2) noexcept { if (x1 < x2) { pathXMin = jmin (pathXMin, x1); pathXMax = jmax (pathXMax, x2); } else { pathXMin = jmin (pathXMin, x2); pathXMax = jmax (pathXMax, x1); } if (y1 < y2) { pathYMin = jmin (pathYMin, y1); pathYMax = jmax (pathYMax, y2); } else { pathYMin = jmin (pathYMin, y2); pathYMax = jmax (pathYMax, y1); } } //============================================================================== Path::Path() : numElements (0), useNonZeroWinding (true) { } Path::~Path() { } Path::Path (const Path& other) : numElements (other.numElements), bounds (other.bounds), useNonZeroWinding (other.useNonZeroWinding) { if (numElements > 0) { data.setAllocatedSize ((int) numElements); memcpy (data.elements, other.data.elements, numElements * sizeof (float)); } } Path& Path::operator= (const Path& other) { if (this != &other) { data.ensureAllocatedSize ((int) other.numElements); numElements = other.numElements; bounds = other.bounds; useNonZeroWinding = other.useNonZeroWinding; if (numElements > 0) memcpy (data.elements, other.data.elements, numElements * sizeof (float)); } return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Path::Path (Path&& other) noexcept : data (static_cast &&> (other.data)), numElements (other.numElements), bounds (other.bounds), useNonZeroWinding (other.useNonZeroWinding) { } Path& Path::operator= (Path&& other) noexcept { data = static_cast &&> (other.data); numElements = other.numElements; bounds = other.bounds; useNonZeroWinding = other.useNonZeroWinding; return *this; } #endif bool Path::operator== (const Path& other) const noexcept { return ! operator!= (other); } bool Path::operator!= (const Path& other) const noexcept { if (numElements != other.numElements || useNonZeroWinding != other.useNonZeroWinding) return true; for (size_t i = 0; i < numElements; ++i) if (data.elements[i] != other.data.elements[i]) return true; return false; } void Path::clear() noexcept { numElements = 0; bounds.reset(); } void Path::swapWithPath (Path& other) noexcept { data.swapWith (other.data); std::swap (numElements, other.numElements); std::swap (bounds.pathXMin, other.bounds.pathXMin); std::swap (bounds.pathXMax, other.bounds.pathXMax); std::swap (bounds.pathYMin, other.bounds.pathYMin); std::swap (bounds.pathYMax, other.bounds.pathYMax); std::swap (useNonZeroWinding, other.useNonZeroWinding); } //============================================================================== void Path::setUsingNonZeroWinding (const bool isNonZero) noexcept { useNonZeroWinding = isNonZero; } void Path::scaleToFit (const float x, const float y, const float w, const float h, const bool preserveProportions) noexcept { applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions)); } //============================================================================== bool Path::isEmpty() const noexcept { size_t i = 0; while (i < numElements) { const float type = data.elements [i++]; if (type == moveMarker) { i += 2; } else if (type == lineMarker || type == quadMarker || type == cubicMarker) { return false; } } return true; } Rectangle Path::getBounds() const noexcept { return bounds.getRectangle(); } Rectangle Path::getBoundsTransformed (const AffineTransform& transform) const noexcept { return getBounds().transformedBy (transform); } //============================================================================== void Path::preallocateSpace (int numExtraCoordsToMakeSpaceFor) { data.ensureAllocatedSize ((int) numElements + numExtraCoordsToMakeSpaceFor); } void Path::startNewSubPath (const float x, const float y) { JUCE_CHECK_COORDS_ARE_VALID (x, y); if (numElements == 0) bounds.reset (x, y); else bounds.extend (x, y); preallocateSpace (3); data.elements [numElements++] = moveMarker; data.elements [numElements++] = x; data.elements [numElements++] = y; } void Path::startNewSubPath (const Point start) { startNewSubPath (start.x, start.y); } void Path::lineTo (const float x, const float y) { JUCE_CHECK_COORDS_ARE_VALID (x, y); if (numElements == 0) startNewSubPath (0, 0); preallocateSpace (3); data.elements [numElements++] = lineMarker; data.elements [numElements++] = x; data.elements [numElements++] = y; bounds.extend (x, y); } void Path::lineTo (const Point end) { lineTo (end.x, end.y); } void Path::quadraticTo (const float x1, const float y1, const float x2, const float y2) { JUCE_CHECK_COORDS_ARE_VALID (x1, y1); JUCE_CHECK_COORDS_ARE_VALID (x2, y2); if (numElements == 0) startNewSubPath (0, 0); preallocateSpace (5); data.elements [numElements++] = quadMarker; data.elements [numElements++] = x1; data.elements [numElements++] = y1; data.elements [numElements++] = x2; data.elements [numElements++] = y2; bounds.extend (x1, y1, x2, y2); } void Path::quadraticTo (const Point controlPoint, const Point endPoint) { quadraticTo (controlPoint.x, controlPoint.y, endPoint.x, endPoint.y); } void Path::cubicTo (const float x1, const float y1, const float x2, const float y2, const float x3, const float y3) { JUCE_CHECK_COORDS_ARE_VALID (x1, y1); JUCE_CHECK_COORDS_ARE_VALID (x2, y2); JUCE_CHECK_COORDS_ARE_VALID (x3, y3); if (numElements == 0) startNewSubPath (0, 0); preallocateSpace (7); data.elements [numElements++] = cubicMarker; data.elements [numElements++] = x1; data.elements [numElements++] = y1; data.elements [numElements++] = x2; data.elements [numElements++] = y2; data.elements [numElements++] = x3; data.elements [numElements++] = y3; bounds.extend (x1, y1, x2, y2); bounds.extend (x3, y3); } void Path::cubicTo (const Point controlPoint1, const Point controlPoint2, const Point endPoint) { cubicTo (controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y); } void Path::closeSubPath() { if (numElements > 0 && data.elements [numElements - 1] != closeSubPathMarker) { preallocateSpace (1); data.elements [numElements++] = closeSubPathMarker; } } Point Path::getCurrentPosition() const { int i = (int) numElements - 1; if (i > 0 && data.elements[i] == closeSubPathMarker) { while (i >= 0) { if (data.elements[i] == moveMarker) { i += 2; break; } --i; } } if (i > 0) return Point (data.elements [i - 1], data.elements [i]); return Point(); } void Path::addRectangle (const float x, const float y, const float w, const float h) { float x1 = x, y1 = y, x2 = x + w, y2 = y + h; if (w < 0) std::swap (x1, x2); if (h < 0) std::swap (y1, y2); preallocateSpace (13); if (numElements == 0) { bounds.pathXMin = x1; bounds.pathXMax = x2; bounds.pathYMin = y1; bounds.pathYMax = y2; } else { bounds.pathXMin = jmin (bounds.pathXMin, x1); bounds.pathXMax = jmax (bounds.pathXMax, x2); bounds.pathYMin = jmin (bounds.pathYMin, y1); bounds.pathYMax = jmax (bounds.pathYMax, y2); } data.elements [numElements++] = moveMarker; data.elements [numElements++] = x1; data.elements [numElements++] = y2; data.elements [numElements++] = lineMarker; data.elements [numElements++] = x1; data.elements [numElements++] = y1; data.elements [numElements++] = lineMarker; data.elements [numElements++] = x2; data.elements [numElements++] = y1; data.elements [numElements++] = lineMarker; data.elements [numElements++] = x2; data.elements [numElements++] = y2; data.elements [numElements++] = closeSubPathMarker; } void Path::addRoundedRectangle (float x, float y, float w, float h, float csx, float csy) { addRoundedRectangle (x, y, w, h, csx, csy, true, true, true, true); } void Path::addRoundedRectangle (const float x, const float y, const float w, const float h, float csx, float csy, const bool curveTopLeft, const bool curveTopRight, const bool curveBottomLeft, const bool curveBottomRight) { csx = jmin (csx, w * 0.5f); csy = jmin (csy, h * 0.5f); const float cs45x = csx * 0.45f; const float cs45y = csy * 0.45f; const float x2 = x + w; const float y2 = y + h; if (curveTopLeft) { startNewSubPath (x, y + csy); cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y); } else { startNewSubPath (x, y); } if (curveTopRight) { lineTo (x2 - csx, y); cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy); } else { lineTo (x2, y); } if (curveBottomRight) { lineTo (x2, y2 - csy); cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2); } else { lineTo (x2, y2); } if (curveBottomLeft) { lineTo (x + csx, y2); cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy); } else { lineTo (x, y2); } closeSubPath(); } void Path::addRoundedRectangle (float x, float y, float w, float h, float cs) { addRoundedRectangle (x, y, w, h, cs, cs); } void Path::addTriangle (float x1, float y1, float x2, float y2, float x3, float y3) { addTriangle (Point (x1, y1), Point (x2, y2), Point (x3, y3)); } void Path::addTriangle (Point p1, Point p2, Point p3) { startNewSubPath (p1); lineTo (p2); lineTo (p3); closeSubPath(); } void Path::addQuadrilateral (const float x1, const float y1, const float x2, const float y2, const float x3, const float y3, const float x4, const float y4) { startNewSubPath (x1, y1); lineTo (x2, y2); lineTo (x3, y3); lineTo (x4, y4); closeSubPath(); } void Path::addEllipse (float x, float y, float w, float h) { addEllipse (Rectangle (x, y, w, h)); } void Path::addEllipse (Rectangle area) { const float hw = area.getWidth() * 0.5f; const float hw55 = hw * 0.55f; const float hh = area.getHeight() * 0.5f; const float hh55 = hh * 0.55f; const float cx = area.getX() + hw; const float cy = area.getY() + hh; startNewSubPath (cx, cy - hh); cubicTo (cx + hw55, cy - hh, cx + hw, cy - hh55, cx + hw, cy); cubicTo (cx + hw, cy + hh55, cx + hw55, cy + hh, cx, cy + hh); cubicTo (cx - hw55, cy + hh, cx - hw, cy + hh55, cx - hw, cy); cubicTo (cx - hw, cy - hh55, cx - hw55, cy - hh, cx, cy - hh); closeSubPath(); } void Path::addArc (const float x, const float y, const float w, const float h, const float fromRadians, const float toRadians, const bool startAsNewSubPath) { const float radiusX = w / 2.0f; const float radiusY = h / 2.0f; addCentredArc (x + radiusX, y + radiusY, radiusX, radiusY, 0.0f, fromRadians, toRadians, startAsNewSubPath); } void Path::addCentredArc (const float centreX, const float centreY, const float radiusX, const float radiusY, const float rotationOfEllipse, const float fromRadians, float toRadians, const bool startAsNewSubPath) { if (radiusX > 0.0f && radiusY > 0.0f) { const Point centre (centreX, centreY); const AffineTransform rotation (AffineTransform::rotation (rotationOfEllipse, centreX, centreY)); float angle = fromRadians; if (startAsNewSubPath) startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation)); if (fromRadians < toRadians) { if (startAsNewSubPath) angle += PathHelpers::ellipseAngularIncrement; while (angle < toRadians) { lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation)); angle += PathHelpers::ellipseAngularIncrement; } } else { if (startAsNewSubPath) angle -= PathHelpers::ellipseAngularIncrement; while (angle > toRadians) { lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation)); angle -= PathHelpers::ellipseAngularIncrement; } } lineTo (centre.getPointOnCircumference (radiusX, radiusY, toRadians).transformedBy (rotation)); } } void Path::addPieSegment (const float x, const float y, const float width, const float height, const float fromRadians, const float toRadians, const float innerCircleProportionalSize) { float radiusX = width * 0.5f; float radiusY = height * 0.5f; const Point centre (x + radiusX, y + radiusY); startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, fromRadians)); addArc (x, y, width, height, fromRadians, toRadians); if (std::abs (fromRadians - toRadians) > float_Pi * 1.999f) { closeSubPath(); if (innerCircleProportionalSize > 0) { radiusX *= innerCircleProportionalSize; radiusY *= innerCircleProportionalSize; startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, toRadians)); addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians); } } else { if (innerCircleProportionalSize > 0) { radiusX *= innerCircleProportionalSize; radiusY *= innerCircleProportionalSize; addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians); } else { lineTo (centre); } } closeSubPath(); } void Path::addPieSegment (Rectangle segmentBounds, const float fromRadians, const float toRadians, const float innerCircleProportionalSize) { addPieSegment (segmentBounds.getX(), segmentBounds.getY(), segmentBounds.getWidth(), segmentBounds.getHeight(), fromRadians, toRadians, innerCircleProportionalSize); } //============================================================================== void Path::addLineSegment (const Line& line, float lineThickness) { const Line reversed (line.reversed()); lineThickness *= 0.5f; startNewSubPath (line.getPointAlongLine (0, lineThickness)); lineTo (line.getPointAlongLine (0, -lineThickness)); lineTo (reversed.getPointAlongLine (0, lineThickness)); lineTo (reversed.getPointAlongLine (0, -lineThickness)); closeSubPath(); } void Path::addArrow (const Line& line, float lineThickness, float arrowheadWidth, float arrowheadLength) { const Line reversed (line.reversed()); lineThickness *= 0.5f; arrowheadWidth *= 0.5f; arrowheadLength = jmin (arrowheadLength, 0.8f * line.getLength()); startNewSubPath (line.getPointAlongLine (0, lineThickness)); lineTo (line.getPointAlongLine (0, -lineThickness)); lineTo (reversed.getPointAlongLine (arrowheadLength, lineThickness)); lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth)); lineTo (line.getEnd()); lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth)); lineTo (reversed.getPointAlongLine (arrowheadLength, -lineThickness)); closeSubPath(); } void Path::addPolygon (const Point centre, const int numberOfSides, const float radius, const float startAngle) { jassert (numberOfSides > 1); // this would be silly. if (numberOfSides > 1) { const float angleBetweenPoints = float_Pi * 2.0f / numberOfSides; for (int i = 0; i < numberOfSides; ++i) { const float angle = startAngle + i * angleBetweenPoints; const Point p (centre.getPointOnCircumference (radius, angle)); if (i == 0) startNewSubPath (p); else lineTo (p); } closeSubPath(); } } void Path::addStar (const Point centre, const int numberOfPoints, const float innerRadius, const float outerRadius, const float startAngle) { jassert (numberOfPoints > 1); // this would be silly. if (numberOfPoints > 1) { const float angleBetweenPoints = float_Pi * 2.0f / numberOfPoints; for (int i = 0; i < numberOfPoints; ++i) { const float angle = startAngle + i * angleBetweenPoints; const Point p (centre.getPointOnCircumference (outerRadius, angle)); if (i == 0) startNewSubPath (p); else lineTo (p); lineTo (centre.getPointOnCircumference (innerRadius, angle + angleBetweenPoints * 0.5f)); } closeSubPath(); } } void Path::addBubble (const Rectangle& bodyArea, const Rectangle& maximumArea, const Point arrowTip, const float cornerSize, const float arrowBaseWidth) { const float halfW = bodyArea.getWidth() / 2.0f; const float halfH = bodyArea.getHeight() / 2.0f; const float cornerSizeW = jmin (cornerSize, halfW); const float cornerSizeH = jmin (cornerSize, halfH); const float cornerSizeW2 = 2.0f * cornerSizeW; const float cornerSizeH2 = 2.0f * cornerSizeH; startNewSubPath (bodyArea.getX() + cornerSizeW, bodyArea.getY()); const Rectangle targetLimit (bodyArea.reduced (jmin (halfW - 1.0f, cornerSizeW + arrowBaseWidth), jmin (halfH - 1.0f, cornerSizeH + arrowBaseWidth))); if (Rectangle (targetLimit.getX(), maximumArea.getY(), targetLimit.getWidth(), bodyArea.getY() - maximumArea.getY()).contains (arrowTip)) { lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getY()); lineTo (arrowTip.x, arrowTip.y); lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getY()); } lineTo (bodyArea.getRight() - cornerSizeW, bodyArea.getY()); addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getY(), cornerSizeW2, cornerSizeH2, 0, float_Pi * 0.5f); if (Rectangle (bodyArea.getRight(), targetLimit.getY(), maximumArea.getRight() - bodyArea.getRight(), targetLimit.getHeight()).contains (arrowTip)) { lineTo (bodyArea.getRight(), arrowTip.y - arrowBaseWidth); lineTo (arrowTip.x, arrowTip.y); lineTo (bodyArea.getRight(), arrowTip.y + arrowBaseWidth); } lineTo (bodyArea.getRight(), bodyArea.getBottom() - cornerSizeH); addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, float_Pi * 0.5f, float_Pi); if (Rectangle (targetLimit.getX(), bodyArea.getBottom(), targetLimit.getWidth(), maximumArea.getBottom() - bodyArea.getBottom()).contains (arrowTip)) { lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getBottom()); lineTo (arrowTip.x, arrowTip.y); lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getBottom()); } lineTo (bodyArea.getX() + cornerSizeW, bodyArea.getBottom()); addArc (bodyArea.getX(), bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, float_Pi, float_Pi * 1.5f); if (Rectangle (maximumArea.getX(), targetLimit.getY(), bodyArea.getX() - maximumArea.getX(), targetLimit.getHeight()).contains (arrowTip)) { lineTo (bodyArea.getX(), arrowTip.y + arrowBaseWidth); lineTo (arrowTip.x, arrowTip.y); lineTo (bodyArea.getX(), arrowTip.y - arrowBaseWidth); } lineTo (bodyArea.getX(), bodyArea.getY() + cornerSizeH); addArc (bodyArea.getX(), bodyArea.getY(), cornerSizeW2, cornerSizeH2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f); closeSubPath(); } void Path::addPath (const Path& other) { size_t i = 0; const float* const d = other.data.elements; while (i < other.numElements) { const float type = d[i++]; if (type == moveMarker) { startNewSubPath (d[i], d[i + 1]); i += 2; } else if (type == lineMarker) { lineTo (d[i], d[i + 1]); i += 2; } else if (type == quadMarker) { quadraticTo (d[i], d[i + 1], d[i + 2], d[i + 3]); i += 4; } else if (type == cubicMarker) { cubicTo (d[i], d[i + 1], d[i + 2], d[i + 3], d[i + 4], d[i + 5]); i += 6; } else if (type == closeSubPathMarker) { closeSubPath(); } else { // something's gone wrong with the element list! jassertfalse; } } } void Path::addPath (const Path& other, const AffineTransform& transformToApply) { size_t i = 0; const float* const d = other.data.elements; while (i < other.numElements) { const float type = d [i++]; if (type == closeSubPathMarker) { closeSubPath(); } else { float x = d[i++]; float y = d[i++]; transformToApply.transformPoint (x, y); if (type == moveMarker) { startNewSubPath (x, y); } else if (type == lineMarker) { lineTo (x, y); } else if (type == quadMarker) { float x2 = d [i++]; float y2 = d [i++]; transformToApply.transformPoint (x2, y2); quadraticTo (x, y, x2, y2); } else if (type == cubicMarker) { float x2 = d [i++]; float y2 = d [i++]; float x3 = d [i++]; float y3 = d [i++]; transformToApply.transformPoints (x2, y2, x3, y3); cubicTo (x, y, x2, y2, x3, y3); } else { // something's gone wrong with the element list! jassertfalse; } } } } //============================================================================== void Path::applyTransform (const AffineTransform& transform) noexcept { bounds.reset(); bool firstPoint = true; float* d = data.elements; float* const end = d + numElements; while (d < end) { const float type = *d++; if (type == moveMarker) { transform.transformPoint (d[0], d[1]); if (firstPoint) { firstPoint = false; bounds.reset (d[0], d[1]); } else { bounds.extend (d[0], d[1]); } d += 2; } else if (type == lineMarker) { transform.transformPoint (d[0], d[1]); bounds.extend (d[0], d[1]); d += 2; } else if (type == quadMarker) { transform.transformPoints (d[0], d[1], d[2], d[3]); bounds.extend (d[0], d[1], d[2], d[3]); d += 4; } else if (type == cubicMarker) { transform.transformPoints (d[0], d[1], d[2], d[3], d[4], d[5]); bounds.extend (d[0], d[1], d[2], d[3]); bounds.extend (d[4], d[5]); d += 6; } } } //============================================================================== AffineTransform Path::getTransformToScaleToFit (const Rectangle& area, bool preserveProportions, Justification justification) const { return getTransformToScaleToFit (area.getX(), area.getY(), area.getWidth(), area.getHeight(), preserveProportions, justification); } AffineTransform Path::getTransformToScaleToFit (const float x, const float y, const float w, const float h, const bool preserveProportions, Justification justification) const { Rectangle boundsRect (getBounds()); if (preserveProportions) { if (w <= 0 || h <= 0 || boundsRect.isEmpty()) return AffineTransform::identity; float newW, newH; const float srcRatio = boundsRect.getHeight() / boundsRect.getWidth(); if (srcRatio > h / w) { newW = h / srcRatio; newH = h; } else { newW = w; newH = w * srcRatio; } float newXCentre = x; float newYCentre = y; if (justification.testFlags (Justification::left)) newXCentre += newW * 0.5f; else if (justification.testFlags (Justification::right)) newXCentre += w - newW * 0.5f; else newXCentre += w * 0.5f; if (justification.testFlags (Justification::top)) newYCentre += newH * 0.5f; else if (justification.testFlags (Justification::bottom)) newYCentre += h - newH * 0.5f; else newYCentre += h * 0.5f; return AffineTransform::translation (boundsRect.getWidth() * -0.5f - boundsRect.getX(), boundsRect.getHeight() * -0.5f - boundsRect.getY()) .scaled (newW / boundsRect.getWidth(), newH / boundsRect.getHeight()) .translated (newXCentre, newYCentre); } else { return AffineTransform::translation (-boundsRect.getX(), -boundsRect.getY()) .scaled (w / boundsRect.getWidth(), h / boundsRect.getHeight()) .translated (x, y); } } //============================================================================== bool Path::contains (const float x, const float y, const float tolerance) const { if (x <= bounds.pathXMin || x >= bounds.pathXMax || y <= bounds.pathYMin || y >= bounds.pathYMax) return false; PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); int positiveCrossings = 0; int negativeCrossings = 0; while (i.next()) { if ((i.y1 <= y && i.y2 > y) || (i.y2 <= y && i.y1 > y)) { const float intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1); if (intersectX <= x) { if (i.y1 < i.y2) ++positiveCrossings; else ++negativeCrossings; } } } return useNonZeroWinding ? (negativeCrossings != positiveCrossings) : ((negativeCrossings + positiveCrossings) & 1) != 0; } bool Path::contains (const Point point, const float tolerance) const { return contains (point.x, point.y, tolerance); } bool Path::intersectsLine (const Line& line, const float tolerance) { PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); Point intersection; while (i.next()) if (line.intersects (Line (i.x1, i.y1, i.x2, i.y2), intersection)) return true; return false; } Line Path::getClippedLine (const Line& line, const bool keepSectionOutsidePath) const { Line result (line); const bool startInside = contains (line.getStart()); const bool endInside = contains (line.getEnd()); if (startInside == endInside) { if (keepSectionOutsidePath == startInside) result = Line(); } else { PathFlatteningIterator i (*this, AffineTransform::identity); Point intersection; while (i.next()) { if (line.intersects (Line (i.x1, i.y1, i.x2, i.y2), intersection)) { if ((startInside && keepSectionOutsidePath) || (endInside && ! keepSectionOutsidePath)) result.setStart (intersection); else result.setEnd (intersection); } } } return result; } float Path::getLength (const AffineTransform& transform) const { float length = 0; PathFlatteningIterator i (*this, transform); while (i.next()) length += Line (i.x1, i.y1, i.x2, i.y2).getLength(); return length; } Point Path::getPointAlongPath (float distanceFromStart, const AffineTransform& transform) const { PathFlatteningIterator i (*this, transform); while (i.next()) { const Line line (i.x1, i.y1, i.x2, i.y2); const float lineLength = line.getLength(); if (distanceFromStart <= lineLength) return line.getPointAlongLine (distanceFromStart); distanceFromStart -= lineLength; } return Point (i.x2, i.y2); } float Path::getNearestPoint (const Point targetPoint, Point& pointOnPath, const AffineTransform& transform) const { PathFlatteningIterator i (*this, transform); float bestPosition = 0, bestDistance = std::numeric_limits::max(); float length = 0; Point pointOnLine; while (i.next()) { const Line line (i.x1, i.y1, i.x2, i.y2); const float distance = line.getDistanceFromPoint (targetPoint, pointOnLine); if (distance < bestDistance) { bestDistance = distance; bestPosition = length + pointOnLine.getDistanceFrom (line.getStart()); pointOnPath = pointOnLine; } length += line.getLength(); } return bestPosition; } //============================================================================== Path Path::createPathWithRoundedCorners (const float cornerRadius) const { if (cornerRadius <= 0.01f) return *this; size_t indexOfPathStart = 0, indexOfPathStartThis = 0; size_t n = 0; bool lastWasLine = false, firstWasLine = false; Path p; while (n < numElements) { const float type = data.elements [n++]; if (type == moveMarker) { indexOfPathStart = p.numElements; indexOfPathStartThis = n - 1; const float x = data.elements [n++]; const float y = data.elements [n++]; p.startNewSubPath (x, y); lastWasLine = false; firstWasLine = (data.elements [n] == lineMarker); } else if (type == lineMarker || type == closeSubPathMarker) { float startX = 0, startY = 0, joinX = 0, joinY = 0, endX, endY; if (type == lineMarker) { endX = data.elements [n++]; endY = data.elements [n++]; if (n > 8) { startX = data.elements [n - 8]; startY = data.elements [n - 7]; joinX = data.elements [n - 5]; joinY = data.elements [n - 4]; } } else { endX = data.elements [indexOfPathStartThis + 1]; endY = data.elements [indexOfPathStartThis + 2]; if (n > 6) { startX = data.elements [n - 6]; startY = data.elements [n - 5]; joinX = data.elements [n - 3]; joinY = data.elements [n - 2]; } } if (lastWasLine) { const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY); if (len1 > 0) { const double propNeeded = jmin (0.5, cornerRadius / len1); p.data.elements [p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded); p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); } const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY); if (len2 > 0) { const double propNeeded = jmin (0.5, cornerRadius / len2); p.quadraticTo (joinX, joinY, (float) (joinX + (endX - joinX) * propNeeded), (float) (joinY + (endY - joinY) * propNeeded)); } p.lineTo (endX, endY); } else if (type == lineMarker) { p.lineTo (endX, endY); lastWasLine = true; } if (type == closeSubPathMarker) { if (firstWasLine) { startX = data.elements [n - 3]; startY = data.elements [n - 2]; joinX = endX; joinY = endY; endX = data.elements [indexOfPathStartThis + 4]; endY = data.elements [indexOfPathStartThis + 5]; const double len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY); if (len1 > 0) { const double propNeeded = jmin (0.5, cornerRadius / len1); p.data.elements [p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded); p.data.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); } const double len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY); if (len2 > 0) { const double propNeeded = jmin (0.5, cornerRadius / len2); endX = (float) (joinX + (endX - joinX) * propNeeded); endY = (float) (joinY + (endY - joinY) * propNeeded); p.quadraticTo (joinX, joinY, endX, endY); p.data.elements [indexOfPathStart + 1] = endX; p.data.elements [indexOfPathStart + 2] = endY; } } p.closeSubPath(); } } else if (type == quadMarker) { lastWasLine = false; const float x1 = data.elements [n++]; const float y1 = data.elements [n++]; const float x2 = data.elements [n++]; const float y2 = data.elements [n++]; p.quadraticTo (x1, y1, x2, y2); } else if (type == cubicMarker) { lastWasLine = false; const float x1 = data.elements [n++]; const float y1 = data.elements [n++]; const float x2 = data.elements [n++]; const float y2 = data.elements [n++]; const float x3 = data.elements [n++]; const float y3 = data.elements [n++]; p.cubicTo (x1, y1, x2, y2, x3, y3); } } return p; } //============================================================================== void Path::loadPathFromStream (InputStream& source) { while (! source.isExhausted()) { switch (source.readByte()) { case 'm': { const float x = source.readFloat(); const float y = source.readFloat(); startNewSubPath (x, y); break; } case 'l': { const float x = source.readFloat(); const float y = source.readFloat(); lineTo (x, y); break; } case 'q': { const float x1 = source.readFloat(); const float y1 = source.readFloat(); const float x2 = source.readFloat(); const float y2 = source.readFloat(); quadraticTo (x1, y1, x2, y2); break; } case 'b': { const float x1 = source.readFloat(); const float y1 = source.readFloat(); const float x2 = source.readFloat(); const float y2 = source.readFloat(); const float x3 = source.readFloat(); const float y3 = source.readFloat(); cubicTo (x1, y1, x2, y2, x3, y3); break; } case 'c': closeSubPath(); break; case 'n': useNonZeroWinding = true; break; case 'z': useNonZeroWinding = false; break; case 'e': return; // end of path marker default: jassertfalse; // illegal char in the stream break; } } } void Path::loadPathFromData (const void* const pathData, const size_t numberOfBytes) { MemoryInputStream in (pathData, numberOfBytes, false); loadPathFromStream (in); } void Path::writePathToStream (OutputStream& dest) const { dest.writeByte (useNonZeroWinding ? 'n' : 'z'); size_t i = 0; while (i < numElements) { const float type = data.elements [i++]; if (type == moveMarker) { dest.writeByte ('m'); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); } else if (type == lineMarker) { dest.writeByte ('l'); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); } else if (type == quadMarker) { dest.writeByte ('q'); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); } else if (type == cubicMarker) { dest.writeByte ('b'); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); dest.writeFloat (data.elements [i++]); } else if (type == closeSubPathMarker) { dest.writeByte ('c'); } } dest.writeByte ('e'); // marks the end-of-path } String Path::toString() const { MemoryOutputStream s (2048); if (! useNonZeroWinding) s << 'a'; size_t i = 0; float lastMarker = 0.0f; while (i < numElements) { const float marker = data.elements [i++]; char markerChar = 0; int numCoords = 0; if (marker == moveMarker) { markerChar = 'm'; numCoords = 2; } else if (marker == lineMarker) { markerChar = 'l'; numCoords = 2; } else if (marker == quadMarker) { markerChar = 'q'; numCoords = 4; } else if (marker == cubicMarker) { markerChar = 'c'; numCoords = 6; } else { jassert (marker == closeSubPathMarker); markerChar = 'z'; } if (marker != lastMarker) { if (s.getDataSize() != 0) s << ' '; s << markerChar; lastMarker = marker; } while (--numCoords >= 0 && i < numElements) { String coord (data.elements [i++], 3); while (coord.endsWithChar ('0') && coord != "0") coord = coord.dropLastCharacters (1); if (coord.endsWithChar ('.')) coord = coord.dropLastCharacters (1); if (s.getDataSize() != 0) s << ' '; s << coord; } } return s.toUTF8(); } void Path::restoreFromString (StringRef stringVersion) { clear(); setUsingNonZeroWinding (true); String::CharPointerType t (stringVersion.text); juce_wchar marker = 'm'; int numValues = 2; float values [6]; for (;;) { const String token (PathHelpers::nextToken (t)); const juce_wchar firstChar = token[0]; int startNum = 0; if (firstChar == 0) break; if (firstChar == 'm' || firstChar == 'l') { marker = firstChar; numValues = 2; } else if (firstChar == 'q') { marker = firstChar; numValues = 4; } else if (firstChar == 'c') { marker = firstChar; numValues = 6; } else if (firstChar == 'z') { marker = firstChar; numValues = 0; } else if (firstChar == 'a') { setUsingNonZeroWinding (false); continue; } else { ++startNum; values [0] = token.getFloatValue(); } for (int i = startNum; i < numValues; ++i) values [i] = PathHelpers::nextToken (t).getFloatValue(); switch (marker) { case 'm': startNewSubPath (values[0], values[1]); break; case 'l': lineTo (values[0], values[1]); break; case 'q': quadraticTo (values[0], values[1], values[2], values[3]); break; case 'c': cubicTo (values[0], values[1], values[2], values[3], values[4], values[5]); break; case 'z': closeSubPath(); break; default: jassertfalse; break; // illegal string format? } } } //============================================================================== Path::Iterator::Iterator (const Path& p) noexcept : x1 (0), y1 (0), x2 (0), y2 (0), x3 (0), y3 (0), path (p), index (0) { } Path::Iterator::~Iterator() noexcept { } bool Path::Iterator::next() noexcept { const float* const elements = path.data.elements; if (index < path.numElements) { const float type = elements [index++]; if (type == moveMarker) { elementType = startNewSubPath; x1 = elements [index++]; y1 = elements [index++]; } else if (type == lineMarker) { elementType = lineTo; x1 = elements [index++]; y1 = elements [index++]; } else if (type == quadMarker) { elementType = quadraticTo; x1 = elements [index++]; y1 = elements [index++]; x2 = elements [index++]; y2 = elements [index++]; } else if (type == cubicMarker) { elementType = cubicTo; x1 = elements [index++]; y1 = elements [index++]; x2 = elements [index++]; y2 = elements [index++]; x3 = elements [index++]; y3 = elements [index++]; } else if (type == closeSubPathMarker) { elementType = closePath; } return true; } return false; } #undef JUCE_CHECK_COORDS_ARE_VALID libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h000066400000000000000000001122041320201440200303200ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PATH_H_INCLUDED #define JUCE_PATH_H_INCLUDED //============================================================================== /** A path is a sequence of lines and curves that may either form a closed shape or be open-ended. To use a path, you can create an empty one, then add lines and curves to it to create shapes, then it can be rendered by a Graphics context or used for geometric operations. e.g. @code Path myPath; myPath.startNewSubPath (10.0f, 10.0f); // move the current position to (10, 10) myPath.lineTo (100.0f, 200.0f); // draw a line from here to (100, 200) myPath.quadraticTo (0.0f, 150.0f, 5.0f, 50.0f); // draw a curve that ends at (5, 50) myPath.closeSubPath(); // close the subpath with a line back to (10, 10) // add an ellipse as well, which will form a second sub-path within the path.. myPath.addEllipse (50.0f, 50.0f, 40.0f, 30.0f); // double the width of the whole thing.. myPath.applyTransform (AffineTransform::scale (2.0f, 1.0f)); // and draw it to a graphics context with a 5-pixel thick outline. g.strokePath (myPath, PathStrokeType (5.0f)); @endcode A path object can actually contain multiple sub-paths, which may themselves be open or closed. @see PathFlatteningIterator, PathStrokeType, Graphics */ class JUCE_API Path { public: //============================================================================== /** Creates an empty path. */ Path(); /** Creates a copy of another path. */ Path (const Path&); /** Destructor. */ ~Path(); /** Copies this path from another one. */ Path& operator= (const Path&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Path (Path&&) noexcept; Path& operator= (Path&&) noexcept; #endif bool operator== (const Path&) const noexcept; bool operator!= (const Path&) const noexcept; //============================================================================== /** Returns true if the path doesn't contain any lines or curves. */ bool isEmpty() const noexcept; /** Returns the smallest rectangle that contains all points within the path. */ Rectangle getBounds() const noexcept; /** Returns the smallest rectangle that contains all points within the path after it's been transformed with the given tranasform matrix. */ Rectangle getBoundsTransformed (const AffineTransform& transform) const noexcept; /** Checks whether a point lies within the path. This is only relevent for closed paths (see closeSubPath()), and may produce false results if used on a path which has open sub-paths. The path's winding rule is taken into account by this method. The tolerance parameter is the maximum error allowed when flattening the path, so this method could return a false positive when your point is up to this distance outside the path's boundary. @see closeSubPath, setUsingNonZeroWinding */ bool contains (float x, float y, float tolerance = 1.0f) const; /** Checks whether a point lies within the path. This is only relevent for closed paths (see closeSubPath()), and may produce false results if used on a path which has open sub-paths. The path's winding rule is taken into account by this method. The tolerance parameter is the maximum error allowed when flattening the path, so this method could return a false positive when your point is up to this distance outside the path's boundary. @see closeSubPath, setUsingNonZeroWinding */ bool contains (const Point point, float tolerance = 1.0f) const; /** Checks whether a line crosses the path. This will return positive if the line crosses any of the paths constituent lines or curves. It doesn't take into account whether the line is inside or outside the path, or whether the path is open or closed. The tolerance parameter is the maximum error allowed when flattening the path, so this method could return a false positive when your point is up to this distance outside the path's boundary. */ bool intersectsLine (const Line& line, float tolerance = 1.0f); /** Cuts off parts of a line to keep the parts that are either inside or outside this path. Note that this isn't smart enough to cope with situations where the line would need to be cut into multiple pieces to correctly clip against a re-entrant shape. @param line the line to clip @param keepSectionOutsidePath if true, it's the section outside the path that will be kept; if false its the section inside the path */ Line getClippedLine (const Line& line, bool keepSectionOutsidePath) const; /** Returns the length of the path. @see getPointAlongPath */ float getLength (const AffineTransform& transform = AffineTransform::identity) const; /** Returns a point that is the specified distance along the path. If the distance is greater than the total length of the path, this will return the end point. @see getLength */ Point getPointAlongPath (float distanceFromStart, const AffineTransform& transform = AffineTransform::identity) const; /** Finds the point along the path which is nearest to a given position. This sets pointOnPath to the nearest point, and returns the distance of this point from the start of the path. */ float getNearestPoint (const Point targetPoint, Point& pointOnPath, const AffineTransform& transform = AffineTransform::identity) const; //============================================================================== /** Removes all lines and curves, resetting the path completely. */ void clear() noexcept; /** Begins a new subpath with a given starting position. This will move the path's current position to the coordinates passed in and make it ready to draw lines or curves starting from this position. After adding whatever lines and curves are needed, you can either close the current sub-path using closeSubPath() or call startNewSubPath() to move to a new sub-path, leaving the old one open-ended. @see lineTo, quadraticTo, cubicTo, closeSubPath */ void startNewSubPath (float startX, float startY); /** Begins a new subpath with a given starting position. This will move the path's current position to the coordinates passed in and make it ready to draw lines or curves starting from this position. After adding whatever lines and curves are needed, you can either close the current sub-path using closeSubPath() or call startNewSubPath() to move to a new sub-path, leaving the old one open-ended. @see lineTo, quadraticTo, cubicTo, closeSubPath */ void startNewSubPath (const Point start); /** Closes a the current sub-path with a line back to its start-point. When creating a closed shape such as a triangle, don't use 3 lineTo() calls - instead use two lineTo() calls, followed by a closeSubPath() to join the final point back to the start. This ensures that closes shapes are recognised as such, and this is important for tasks like drawing strokes, which needs to know whether to draw end-caps or not. @see startNewSubPath, lineTo, quadraticTo, cubicTo, closeSubPath */ void closeSubPath(); /** Adds a line from the shape's last position to a new end-point. This will connect the end-point of the last line or curve that was added to a new point, using a straight line. See the class description for an example of how to add lines and curves to a path. @see startNewSubPath, quadraticTo, cubicTo, closeSubPath */ void lineTo (float endX, float endY); /** Adds a line from the shape's last position to a new end-point. This will connect the end-point of the last line or curve that was added to a new point, using a straight line. See the class description for an example of how to add lines and curves to a path. @see startNewSubPath, quadraticTo, cubicTo, closeSubPath */ void lineTo (const Point end); /** Adds a quadratic bezier curve from the shape's last position to a new position. This will connect the end-point of the last line or curve that was added to a new point, using a quadratic spline with one control-point. See the class description for an example of how to add lines and curves to a path. @see startNewSubPath, lineTo, cubicTo, closeSubPath */ void quadraticTo (float controlPointX, float controlPointY, float endPointX, float endPointY); /** Adds a quadratic bezier curve from the shape's last position to a new position. This will connect the end-point of the last line or curve that was added to a new point, using a quadratic spline with one control-point. See the class description for an example of how to add lines and curves to a path. @see startNewSubPath, lineTo, cubicTo, closeSubPath */ void quadraticTo (const Point controlPoint, const Point endPoint); /** Adds a cubic bezier curve from the shape's last position to a new position. This will connect the end-point of the last line or curve that was added to a new point, using a cubic spline with two control-points. See the class description for an example of how to add lines and curves to a path. @see startNewSubPath, lineTo, quadraticTo, closeSubPath */ void cubicTo (float controlPoint1X, float controlPoint1Y, float controlPoint2X, float controlPoint2Y, float endPointX, float endPointY); /** Adds a cubic bezier curve from the shape's last position to a new position. This will connect the end-point of the last line or curve that was added to a new point, using a cubic spline with two control-points. See the class description for an example of how to add lines and curves to a path. @see startNewSubPath, lineTo, quadraticTo, closeSubPath */ void cubicTo (const Point controlPoint1, const Point controlPoint2, const Point endPoint); /** Returns the last point that was added to the path by one of the drawing methods. */ Point getCurrentPosition() const; //============================================================================== /** Adds a rectangle to the path. The rectangle is added as a new sub-path. (Any currently open paths will be left open). @see addRoundedRectangle, addTriangle */ void addRectangle (float x, float y, float width, float height); /** Adds a rectangle to the path. The rectangle is added as a new sub-path. (Any currently open paths will be left open). @see addRoundedRectangle, addTriangle */ template void addRectangle (const Rectangle& rectangle) { addRectangle (static_cast (rectangle.getX()), static_cast (rectangle.getY()), static_cast (rectangle.getWidth()), static_cast (rectangle.getHeight())); } /** Adds a rectangle with rounded corners to the path. The rectangle is added as a new sub-path. (Any currently open paths will be left open). @see addRectangle, addTriangle */ void addRoundedRectangle (float x, float y, float width, float height, float cornerSize); /** Adds a rectangle with rounded corners to the path. The rectangle is added as a new sub-path. (Any currently open paths will be left open). @see addRectangle, addTriangle */ void addRoundedRectangle (float x, float y, float width, float height, float cornerSizeX, float cornerSizeY); /** Adds a rectangle with rounded corners to the path. The rectangle is added as a new sub-path. (Any currently open paths will be left open). @see addRectangle, addTriangle */ void addRoundedRectangle (float x, float y, float width, float height, float cornerSizeX, float cornerSizeY, bool curveTopLeft, bool curveTopRight, bool curveBottomLeft, bool curveBottomRight); /** Adds a rectangle with rounded corners to the path. The rectangle is added as a new sub-path. (Any currently open paths will be left open). @see addRectangle, addTriangle */ template void addRoundedRectangle (const Rectangle& rectangle, float cornerSizeX, float cornerSizeY) { addRoundedRectangle (static_cast (rectangle.getX()), static_cast (rectangle.getY()), static_cast (rectangle.getWidth()), static_cast (rectangle.getHeight()), cornerSizeX, cornerSizeY); } /** Adds a rectangle with rounded corners to the path. The rectangle is added as a new sub-path. (Any currently open paths will be left open). @see addRectangle, addTriangle */ template void addRoundedRectangle (const Rectangle& rectangle, float cornerSize) { addRoundedRectangle (rectangle, cornerSize, cornerSize); } /** Adds a triangle to the path. The triangle is added as a new closed sub-path. (Any currently open paths will be left open). Note that whether the vertices are specified in clockwise or anticlockwise order will affect how the triangle is filled when it overlaps other shapes (the winding order setting will affect this of course). */ void addTriangle (float x1, float y1, float x2, float y2, float x3, float y3); /** Adds a triangle to the path. The triangle is added as a new closed sub-path. (Any currently open paths will be left open). Note that whether the vertices are specified in clockwise or anticlockwise order will affect how the triangle is filled when it overlaps other shapes (the winding order setting will affect this of course). */ void addTriangle (Point point1, Point point2, Point point3); /** Adds a quadrilateral to the path. The quad is added as a new closed sub-path. (Any currently open paths will be left open). Note that whether the vertices are specified in clockwise or anticlockwise order will affect how the quad is filled when it overlaps other shapes (the winding order setting will affect this of course). */ void addQuadrilateral (float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); /** Adds an ellipse to the path. The shape is added as a new sub-path. (Any currently open paths will be left open). @see addArc */ void addEllipse (float x, float y, float width, float height); /** Adds an ellipse to the path. The shape is added as a new sub-path. (Any currently open paths will be left open). @see addArc */ void addEllipse (Rectangle area); /** Adds an elliptical arc to the current path. Note that when specifying the start and end angles, the curve will be drawn either clockwise or anti-clockwise according to whether the end angle is greater than the start. This means that sometimes you may need to use values greater than 2*Pi for the end angle. @param x the left-hand edge of the rectangle in which the elliptical outline fits @param y the top edge of the rectangle in which the elliptical outline fits @param width the width of the rectangle in which the elliptical outline fits @param height the height of the rectangle in which the elliptical outline fits @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the top-centre of the ellipse) @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the top-centre of the ellipse). This angle can be greater than 2*Pi, so for example to draw a curve clockwise from the 9 o'clock position to the 3 o'clock position via 12 o'clock, you'd use 1.5*Pi and 2.5*Pi as the start and finish points. @param startAsNewSubPath if true, the arc will begin a new subpath from its starting point; if false, it will be added to the current sub-path, continuing from the current postition @see addCentredArc, arcTo, addPieSegment, addEllipse */ void addArc (float x, float y, float width, float height, float fromRadians, float toRadians, bool startAsNewSubPath = false); /** Adds an arc which is centred at a given point, and can have a rotation specified. Note that when specifying the start and end angles, the curve will be drawn either clockwise or anti-clockwise according to whether the end angle is greater than the start. This means that sometimes you may need to use values greater than 2*Pi for the end angle. @param centreX the centre x of the ellipse @param centreY the centre y of the ellipse @param radiusX the horizontal radius of the ellipse @param radiusY the vertical radius of the ellipse @param rotationOfEllipse an angle by which the whole ellipse should be rotated about its centre, in radians (clockwise) @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the top-centre of the ellipse) @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the top-centre of the ellipse). This angle can be greater than 2*Pi, so for example to draw a curve clockwise from the 9 o'clock position to the 3 o'clock position via 12 o'clock, you'd use 1.5*Pi and 2.5*Pi as the start and finish points. @param startAsNewSubPath if true, the arc will begin a new subpath from its starting point; if false, it will be added to the current sub-path, continuing from the current postition @see addArc, arcTo */ void addCentredArc (float centreX, float centreY, float radiusX, float radiusY, float rotationOfEllipse, float fromRadians, float toRadians, bool startAsNewSubPath = false); /** Adds a "pie-chart" shape to the path. The shape is added as a new sub-path. (Any currently open paths will be left open). Note that when specifying the start and end angles, the curve will be drawn either clockwise or anti-clockwise according to whether the end angle is greater than the start. This means that sometimes you may need to use values greater than 2*Pi for the end angle. @param x the left-hand edge of the rectangle in which the elliptical outline fits @param y the top edge of the rectangle in which the elliptical outline fits @param width the width of the rectangle in which the elliptical outline fits @param height the height of the rectangle in which the elliptical outline fits @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the top-centre of the ellipse) @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the top-centre of the ellipse) @param innerCircleProportionalSize if this is > 0, then the pie will be drawn as a curved band around a hollow ellipse at its centre, where this value indicates the inner ellipse's size with respect to the outer one. @see addArc */ void addPieSegment (float x, float y, float width, float height, float fromRadians, float toRadians, float innerCircleProportionalSize); /** Adds a "pie-chart" shape to the path. The shape is added as a new sub-path. (Any currently open paths will be left open). Note that when specifying the start and end angles, the curve will be drawn either clockwise or anti-clockwise according to whether the end angle is greater than the start. This means that sometimes you may need to use values greater than 2*Pi for the end angle. @param segmentBounds the outer rectangle in which the elliptical outline fits @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the top-centre of the ellipse) @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the top-centre of the ellipse) @param innerCircleProportionalSize if this is > 0, then the pie will be drawn as a curved band around a hollow ellipse at its centre, where this value indicates the inner ellipse's size with respect to the outer one. @see addArc */ void addPieSegment (Rectangle segmentBounds, float fromRadians, float toRadians, float innerCircleProportionalSize); /** Adds a line with a specified thickness. The line is added as a new closed sub-path. (Any currently open paths will be left open). @see addArrow */ void addLineSegment (const Line& line, float lineThickness); /** Adds a line with an arrowhead on the end. The arrow is added as a new closed sub-path. (Any currently open paths will be left open). @see PathStrokeType::createStrokeWithArrowheads */ void addArrow (const Line& line, float lineThickness, float arrowheadWidth, float arrowheadLength); /** Adds a polygon shape to the path. @see addStar */ void addPolygon (const Point centre, int numberOfSides, float radius, float startAngle = 0.0f); /** Adds a star shape to the path. @see addPolygon */ void addStar (const Point centre, int numberOfPoints, float innerRadius, float outerRadius, float startAngle = 0.0f); /** Adds a speech-bubble shape to the path. @param bodyArea the area of the body of the bubble shape @param maximumArea an area which encloses the body area and defines the limits within which the arrow tip can be drawn - if the tip lies outside this area, the bubble will be drawn without an arrow @param arrowTipPosition the location of the tip of the arrow @param cornerSize the size of the rounded corners @param arrowBaseWidth the width of the base of the arrow where it joins the main rectangle */ void addBubble (const Rectangle& bodyArea, const Rectangle& maximumArea, const Point arrowTipPosition, const float cornerSize, const float arrowBaseWidth); /** Adds another path to this one. The new path is added as a new sub-path. (Any currently open paths in this path will be left open). @param pathToAppend the path to add */ void addPath (const Path& pathToAppend); /** Adds another path to this one, transforming it on the way in. The new path is added as a new sub-path, its points being transformed by the given matrix before being added. @param pathToAppend the path to add @param transformToApply an optional transform to apply to the incoming vertices */ void addPath (const Path& pathToAppend, const AffineTransform& transformToApply); /** Swaps the contents of this path with another one. The internal data of the two paths is swapped over, so this is much faster than copying it to a temp variable and back. */ void swapWithPath (Path&) noexcept; //============================================================================== /** Preallocates enough space for adding the given number of coordinates to the path. If you're about to add a large number of lines or curves to the path, it can make the task much more efficient to call this first and avoid costly reallocations as the structure grows. The actual value to pass is a bit tricky to calculate because the space required depends on what you're adding - e.g. each lineTo() or startNewSubPath() will require 3 coords (x, y and a type marker). Each quadraticTo() will need 5, and a cubicTo() will require 7. Closing a sub-path will require 1. */ void preallocateSpace (int numExtraCoordsToMakeSpaceFor); //============================================================================== /** Applies a 2D transform to all the vertices in the path. @see AffineTransform, scaleToFit, getTransformToScaleToFit */ void applyTransform (const AffineTransform& transform) noexcept; /** Rescales this path to make it fit neatly into a given space. This is effectively a quick way of calling applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions)) @param x the x position of the rectangle to fit the path inside @param y the y position of the rectangle to fit the path inside @param width the width of the rectangle to fit the path inside @param height the height of the rectangle to fit the path inside @param preserveProportions if true, it will fit the path into the space without altering its horizontal/vertical scale ratio; if false, it will distort the path to fill the specified ratio both horizontally and vertically @see applyTransform, getTransformToScaleToFit */ void scaleToFit (float x, float y, float width, float height, bool preserveProportions) noexcept; /** Returns a transform that can be used to rescale the path to fit into a given space. @param x the x position of the rectangle to fit the path inside @param y the y position of the rectangle to fit the path inside @param width the width of the rectangle to fit the path inside @param height the height of the rectangle to fit the path inside @param preserveProportions if true, it will fit the path into the space without altering its horizontal/vertical scale ratio; if false, it will distort the path to fill the specified ratio both horizontally and vertically @param justificationType if the proportions are preseved, the resultant path may be smaller than the available rectangle, so this describes how it should be positioned within the space. @returns an appropriate transformation @see applyTransform, scaleToFit */ AffineTransform getTransformToScaleToFit (float x, float y, float width, float height, bool preserveProportions, Justification justificationType = Justification::centred) const; /** Returns a transform that can be used to rescale the path to fit into a given space. @param area the rectangle to fit the path inside @param preserveProportions if true, it will fit the path into the space without altering its horizontal/vertical scale ratio; if false, it will distort the path to fill the specified ratio both horizontally and vertically @param justificationType if the proportions are preseved, the resultant path may be smaller than the available rectangle, so this describes how it should be positioned within the space. @returns an appropriate transformation @see applyTransform, scaleToFit */ AffineTransform getTransformToScaleToFit (const Rectangle& area, bool preserveProportions, Justification justificationType = Justification::centred) const; /** Creates a version of this path where all sharp corners have been replaced by curves. Wherever two lines meet at an angle, this will replace the corner with a curve of the given radius. */ Path createPathWithRoundedCorners (float cornerRadius) const; //============================================================================== /** Changes the winding-rule to be used when filling the path. If set to true (which is the default), then the path uses a non-zero-winding rule to determine which points are inside the path. If set to false, it uses an alternate-winding rule. The winding-rule comes into play when areas of the shape overlap other areas, and determines whether the overlapping regions are considered to be inside or outside. Changing this value just sets a flag - it doesn't affect the contents of the path. @see isUsingNonZeroWinding */ void setUsingNonZeroWinding (bool isNonZeroWinding) noexcept; /** Returns the flag that indicates whether the path should use a non-zero winding rule. The default for a new path is true. @see setUsingNonZeroWinding */ bool isUsingNonZeroWinding() const { return useNonZeroWinding; } //============================================================================== /** Iterates the lines and curves that a path contains. @see Path, PathFlatteningIterator */ class JUCE_API Iterator { public: //============================================================================== Iterator (const Path& path) noexcept; ~Iterator() noexcept; //============================================================================== /** Moves onto the next element in the path. If this returns false, there are no more elements. If it returns true, the elementType variable will be set to the type of the current element, and some of the x and y variables will be filled in with values. */ bool next() noexcept; //============================================================================== enum PathElementType { startNewSubPath, /**< For this type, x1 and y1 will be set to indicate the first point in the subpath. */ lineTo, /**< For this type, x1 and y1 indicate the end point of the line. */ quadraticTo, /**< For this type, x1, y1, x2, y2 indicate the control point and endpoint of a quadratic curve. */ cubicTo, /**< For this type, x1, y1, x2, y2, x3, y3 indicate the two control points and the endpoint of a cubic curve. */ closePath /**< Indicates that the sub-path is being closed. None of the x or y values are valid in this case. */ }; PathElementType elementType; float x1, y1, x2, y2, x3, y3; //============================================================================== private: const Path& path; size_t index; JUCE_DECLARE_NON_COPYABLE (Iterator) }; //============================================================================== /** Loads a stored path from a data stream. The data in the stream must have been written using writePathToStream(). Note that this will append the stored path to whatever is currently in this path, so you might need to call clear() beforehand. @see loadPathFromData, writePathToStream */ void loadPathFromStream (InputStream& source); /** Loads a stored path from a block of data. This is similar to loadPathFromStream(), but just reads from a block of data. Useful if you're including stored shapes in your code as a block of static data. @see loadPathFromStream, writePathToStream */ void loadPathFromData (const void* data, size_t numberOfBytes); /** Stores the path by writing it out to a stream. After writing out a path, you can reload it using loadPathFromStream(). @see loadPathFromStream, loadPathFromData */ void writePathToStream (OutputStream& destination) const; //============================================================================== /** Creates a string containing a textual representation of this path. @see restoreFromString */ String toString() const; /** Restores this path from a string that was created with the toString() method. @see toString() */ void restoreFromString (StringRef stringVersion); private: //============================================================================== friend class PathFlatteningIterator; friend class Path::Iterator; ArrayAllocationBase data; size_t numElements; struct PathBounds { PathBounds() noexcept; Rectangle getRectangle() const noexcept; void reset() noexcept; void reset (float, float) noexcept; void extend (float, float) noexcept; void extend (float, float, float, float) noexcept; float pathXMin, pathXMax, pathYMin, pathYMax; }; PathBounds bounds; bool useNonZeroWinding; static const float lineMarker; static const float moveMarker; static const float quadMarker; static const float cubicMarker; static const float closeSubPathMarker; JUCE_LEAK_DETECTOR (Path) }; #endif // JUCE_PATH_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp000066400000000000000000000213331320201440200323670ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_MSVC && JUCE_DEBUG #pragma optimize ("t", on) #endif const float PathFlatteningIterator::defaultTolerance = 0.6f; //============================================================================== PathFlatteningIterator::PathFlatteningIterator (const Path& path_, const AffineTransform& transform_, const float tolerance) : x2 (0), y2 (0), closesSubPath (false), subPathIndex (-1), path (path_), transform (transform_), points (path_.data.elements), toleranceSquared (tolerance * tolerance), subPathCloseX (0), subPathCloseY (0), isIdentityTransform (transform_.isIdentity()), stackBase (32), index (0), stackSize (32) { stackPos = stackBase; } PathFlatteningIterator::~PathFlatteningIterator() { } bool PathFlatteningIterator::isLastInSubpath() const noexcept { return stackPos == stackBase.getData() && (index >= path.numElements || points [index] == Path::moveMarker); } bool PathFlatteningIterator::next() { x1 = x2; y1 = y2; float x3 = 0; float y3 = 0; float x4 = 0; float y4 = 0; for (;;) { float type; if (stackPos == stackBase) { if (index >= path.numElements) return false; type = points [index++]; if (type != Path::closeSubPathMarker) { x2 = points [index++]; y2 = points [index++]; if (type == Path::quadMarker) { x3 = points [index++]; y3 = points [index++]; if (! isIdentityTransform) transform.transformPoints (x2, y2, x3, y3); } else if (type == Path::cubicMarker) { x3 = points [index++]; y3 = points [index++]; x4 = points [index++]; y4 = points [index++]; if (! isIdentityTransform) transform.transformPoints (x2, y2, x3, y3, x4, y4); } else { if (! isIdentityTransform) transform.transformPoint (x2, y2); } } } else { type = *--stackPos; if (type != Path::closeSubPathMarker) { x2 = *--stackPos; y2 = *--stackPos; if (type == Path::quadMarker) { x3 = *--stackPos; y3 = *--stackPos; } else if (type == Path::cubicMarker) { x3 = *--stackPos; y3 = *--stackPos; x4 = *--stackPos; y4 = *--stackPos; } } } if (type == Path::lineMarker) { ++subPathIndex; closesSubPath = (stackPos == stackBase) && (index < path.numElements) && (points [index] == Path::closeSubPathMarker) && x2 == subPathCloseX && y2 == subPathCloseY; return true; } if (type == Path::quadMarker) { const size_t offset = (size_t) (stackPos - stackBase); if (offset >= stackSize - 10) { stackSize <<= 1; stackBase.realloc (stackSize); stackPos = stackBase + offset; } const float m1x = (x1 + x2) * 0.5f; const float m1y = (y1 + y2) * 0.5f; const float m2x = (x2 + x3) * 0.5f; const float m2y = (y2 + y3) * 0.5f; const float m3x = (m1x + m2x) * 0.5f; const float m3y = (m1y + m2y) * 0.5f; const float errorX = m3x - x2; const float errorY = m3y - y2; if (errorX * errorX + errorY * errorY > toleranceSquared) { *stackPos++ = y3; *stackPos++ = x3; *stackPos++ = m2y; *stackPos++ = m2x; *stackPos++ = Path::quadMarker; *stackPos++ = m3y; *stackPos++ = m3x; *stackPos++ = m1y; *stackPos++ = m1x; *stackPos++ = Path::quadMarker; } else { *stackPos++ = y3; *stackPos++ = x3; *stackPos++ = Path::lineMarker; *stackPos++ = m3y; *stackPos++ = m3x; *stackPos++ = Path::lineMarker; } jassert (stackPos < stackBase + stackSize); } else if (type == Path::cubicMarker) { const size_t offset = (size_t) (stackPos - stackBase); if (offset >= stackSize - 16) { stackSize <<= 1; stackBase.realloc (stackSize); stackPos = stackBase + offset; } const float m1x = (x1 + x2) * 0.5f; const float m1y = (y1 + y2) * 0.5f; const float m2x = (x3 + x2) * 0.5f; const float m2y = (y3 + y2) * 0.5f; const float m3x = (x3 + x4) * 0.5f; const float m3y = (y3 + y4) * 0.5f; const float m4x = (m1x + m2x) * 0.5f; const float m4y = (m1y + m2y) * 0.5f; const float m5x = (m3x + m2x) * 0.5f; const float m5y = (m3y + m2y) * 0.5f; const float error1X = m4x - x2; const float error1Y = m4y - y2; const float error2X = m5x - x3; const float error2Y = m5y - y3; if (error1X * error1X + error1Y * error1Y > toleranceSquared || error2X * error2X + error2Y * error2Y > toleranceSquared) { *stackPos++ = y4; *stackPos++ = x4; *stackPos++ = m3y; *stackPos++ = m3x; *stackPos++ = m5y; *stackPos++ = m5x; *stackPos++ = Path::cubicMarker; *stackPos++ = (m4y + m5y) * 0.5f; *stackPos++ = (m4x + m5x) * 0.5f; *stackPos++ = m4y; *stackPos++ = m4x; *stackPos++ = m1y; *stackPos++ = m1x; *stackPos++ = Path::cubicMarker; } else { *stackPos++ = y4; *stackPos++ = x4; *stackPos++ = Path::lineMarker; *stackPos++ = m5y; *stackPos++ = m5x; *stackPos++ = Path::lineMarker; *stackPos++ = m4y; *stackPos++ = m4x; *stackPos++ = Path::lineMarker; } } else if (type == Path::closeSubPathMarker) { if (x2 != subPathCloseX || y2 != subPathCloseY) { x1 = x2; y1 = y2; x2 = subPathCloseX; y2 = subPathCloseY; closesSubPath = true; return true; } } else { jassert (type == Path::moveMarker); subPathIndex = -1; subPathCloseX = x1 = x2; subPathCloseY = y1 = y2; } } } #if JUCE_MSVC && JUCE_DEBUG #pragma optimize ("", on) // resets optimisations to the project defaults #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.h000066400000000000000000000103751320201440200320400ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PATHITERATOR_H_INCLUDED #define JUCE_PATHITERATOR_H_INCLUDED //============================================================================== /** Flattens a Path object into a series of straight-line sections. Use one of these to iterate through a Path object, and it will convert all the curves into line sections so it's easy to render or perform geometric operations on. @see Path */ class JUCE_API PathFlatteningIterator { public: //============================================================================== /** Creates a PathFlatteningIterator. After creation, use the next() method to initialise the fields in the object with the first line's position. @param path the path to iterate along @param transform a transform to apply to each point in the path being iterated @param tolerance the amount by which the curves are allowed to deviate from the lines into which they are being broken down - a higher tolerance contains less lines, so can be generated faster, but will be less smooth. */ PathFlatteningIterator (const Path& path, const AffineTransform& transform = AffineTransform::identity, float tolerance = defaultTolerance); /** Destructor. */ ~PathFlatteningIterator(); //============================================================================== /** Fetches the next line segment from the path. This will update the member variables x1, y1, x2, y2, subPathIndex and closesSubPath so that they describe the new line segment. @returns false when there are no more lines to fetch. */ bool next(); float x1; /**< The x position of the start of the current line segment. */ float y1; /**< The y position of the start of the current line segment. */ float x2; /**< The x position of the end of the current line segment. */ float y2; /**< The y position of the end of the current line segment. */ /** Indicates whether the current line segment is closing a sub-path. If the current line is the one that connects the end of a sub-path back to the start again, this will be true. */ bool closesSubPath; /** The index of the current line within the current sub-path. E.g. you can use this to see whether the line is the first one in the subpath by seeing if it's 0. */ int subPathIndex; /** Returns true if the current segment is the last in the current sub-path. */ bool isLastInSubpath() const noexcept; /** This is the default value that should be used for the tolerance value (see the constructor parameters). */ static const float defaultTolerance; private: //============================================================================== const Path& path; const AffineTransform transform; float* points; const float toleranceSquared; float subPathCloseX, subPathCloseY; const bool isIdentityTransform; HeapBlock stackBase; float* stackPos; size_t index, stackSize; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathFlatteningIterator) }; #endif // JUCE_PATHITERATOR_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp000066400000000000000000000640611320201440200327140ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ PathStrokeType::PathStrokeType (float strokeThickness) noexcept : thickness (strokeThickness), jointStyle (mitered), endStyle (butt) { } PathStrokeType::PathStrokeType (float strokeThickness, JointStyle joint, EndCapStyle end) noexcept : thickness (strokeThickness), jointStyle (joint), endStyle (end) { } PathStrokeType::PathStrokeType (const PathStrokeType& other) noexcept : thickness (other.thickness), jointStyle (other.jointStyle), endStyle (other.endStyle) { } PathStrokeType& PathStrokeType::operator= (const PathStrokeType& other) noexcept { thickness = other.thickness; jointStyle = other.jointStyle; endStyle = other.endStyle; return *this; } PathStrokeType::~PathStrokeType() noexcept { } bool PathStrokeType::operator== (const PathStrokeType& other) const noexcept { return thickness == other.thickness && jointStyle == other.jointStyle && endStyle == other.endStyle; } bool PathStrokeType::operator!= (const PathStrokeType& other) const noexcept { return ! operator== (other); } //============================================================================== namespace PathStrokeHelpers { static bool lineIntersection (const float x1, const float y1, const float x2, const float y2, const float x3, const float y3, const float x4, const float y4, float& intersectionX, float& intersectionY, float& distanceBeyondLine1EndSquared) noexcept { if (x2 != x3 || y2 != y3) { const float dx1 = x2 - x1; const float dy1 = y2 - y1; const float dx2 = x4 - x3; const float dy2 = y4 - y3; const float divisor = dx1 * dy2 - dx2 * dy1; if (divisor == 0) { if (! ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0))) { if (dy1 == 0 && dy2 != 0) { const float along = (y1 - y3) / dy2; intersectionX = x3 + along * dx2; intersectionY = y1; distanceBeyondLine1EndSquared = intersectionX - x2; distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; if ((x2 > x1) == (intersectionX < x2)) distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; return along >= 0 && along <= 1.0f; } else if (dy2 == 0 && dy1 != 0) { const float along = (y3 - y1) / dy1; intersectionX = x1 + along * dx1; intersectionY = y3; distanceBeyondLine1EndSquared = (along - 1.0f) * dx1; distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; if (along < 1.0f) distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; return along >= 0 && along <= 1.0f; } else if (dx1 == 0 && dx2 != 0) { const float along = (x1 - x3) / dx2; intersectionX = x1; intersectionY = y3 + along * dy2; distanceBeyondLine1EndSquared = intersectionY - y2; distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; if ((y2 > y1) == (intersectionY < y2)) distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; return along >= 0 && along <= 1.0f; } else if (dx2 == 0 && dx1 != 0) { const float along = (x3 - x1) / dx1; intersectionX = x3; intersectionY = y1 + along * dy1; distanceBeyondLine1EndSquared = (along - 1.0f) * dy1; distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; if (along < 1.0f) distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; return along >= 0 && along <= 1.0f; } } intersectionX = 0.5f * (x2 + x3); intersectionY = 0.5f * (y2 + y3); distanceBeyondLine1EndSquared = 0.0f; return false; } else { const float along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; intersectionX = x1 + along1 * dx1; intersectionY = y1 + along1 * dy1; if (along1 >= 0 && along1 <= 1.0f) { const float along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1); if (along2 >= 0 && along2 <= divisor) { distanceBeyondLine1EndSquared = 0.0f; return true; } } distanceBeyondLine1EndSquared = along1 - 1.0f; distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; distanceBeyondLine1EndSquared *= (dx1 * dx1 + dy1 * dy1); if (along1 < 1.0f) distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; return false; } } intersectionX = x2; intersectionY = y2; distanceBeyondLine1EndSquared = 0.0f; return true; } static void addEdgeAndJoint (Path& destPath, const PathStrokeType::JointStyle style, const float maxMiterExtensionSquared, const float width, const float x1, const float y1, const float x2, const float y2, const float x3, const float y3, const float x4, const float y4, const float midX, const float midY) { if (style == PathStrokeType::beveled || (x3 == x4 && y3 == y4) || (x1 == x2 && y1 == y2)) { destPath.lineTo (x2, y2); destPath.lineTo (x3, y3); } else { float jx, jy, distanceBeyondLine1EndSquared; // if they intersect, use this point.. if (lineIntersection (x1, y1, x2, y2, x3, y3, x4, y4, jx, jy, distanceBeyondLine1EndSquared)) { destPath.lineTo (jx, jy); } else { if (style == PathStrokeType::mitered) { if (distanceBeyondLine1EndSquared < maxMiterExtensionSquared && distanceBeyondLine1EndSquared > 0.0f) { destPath.lineTo (jx, jy); } else { // the end sticks out too far, so just use a blunt joint destPath.lineTo (x2, y2); destPath.lineTo (x3, y3); } } else { // curved joints float angle1 = std::atan2 (x2 - midX, y2 - midY); float angle2 = std::atan2 (x3 - midX, y3 - midY); const float angleIncrement = 0.1f; destPath.lineTo (x2, y2); if (std::abs (angle1 - angle2) > angleIncrement) { if (angle2 > angle1 + float_Pi || (angle2 < angle1 && angle2 >= angle1 - float_Pi)) { if (angle2 > angle1) angle2 -= float_Pi * 2.0f; jassert (angle1 <= angle2 + float_Pi); angle1 -= angleIncrement; while (angle1 > angle2) { destPath.lineTo (midX + width * std::sin (angle1), midY + width * std::cos (angle1)); angle1 -= angleIncrement; } } else { if (angle1 > angle2) angle1 -= float_Pi * 2.0f; jassert (angle1 >= angle2 - float_Pi); angle1 += angleIncrement; while (angle1 < angle2) { destPath.lineTo (midX + width * std::sin (angle1), midY + width * std::cos (angle1)); angle1 += angleIncrement; } } } destPath.lineTo (x3, y3); } } } } static void addLineEnd (Path& destPath, const PathStrokeType::EndCapStyle style, const float x1, const float y1, const float x2, const float y2, const float width) { if (style == PathStrokeType::butt) { destPath.lineTo (x2, y2); } else { float offx1, offy1, offx2, offy2; float dx = x2 - x1; float dy = y2 - y1; const float len = juce_hypot (dx, dy); if (len == 0) { offx1 = offx2 = x1; offy1 = offy2 = y1; } else { const float offset = width / len; dx *= offset; dy *= offset; offx1 = x1 + dy; offy1 = y1 - dx; offx2 = x2 + dy; offy2 = y2 - dx; } if (style == PathStrokeType::square) { // sqaure ends destPath.lineTo (offx1, offy1); destPath.lineTo (offx2, offy2); destPath.lineTo (x2, y2); } else { // rounded ends const float midx = (offx1 + offx2) * 0.5f; const float midy = (offy1 + offy2) * 0.5f; destPath.cubicTo (x1 + (offx1 - x1) * 0.55f, y1 + (offy1 - y1) * 0.55f, offx1 + (midx - offx1) * 0.45f, offy1 + (midy - offy1) * 0.45f, midx, midy); destPath.cubicTo (midx + (offx2 - midx) * 0.55f, midy + (offy2 - midy) * 0.55f, offx2 + (x2 - offx2) * 0.45f, offy2 + (y2 - offy2) * 0.45f, x2, y2); } } } struct Arrowhead { float startWidth, startLength; float endWidth, endLength; }; static void addArrowhead (Path& destPath, const float x1, const float y1, const float x2, const float y2, const float tipX, const float tipY, const float width, const float arrowheadWidth) { Line line (x1, y1, x2, y2); destPath.lineTo (line.getPointAlongLine (-(arrowheadWidth / 2.0f - width), 0)); destPath.lineTo (tipX, tipY); destPath.lineTo (line.getPointAlongLine (arrowheadWidth - (arrowheadWidth / 2.0f - width), 0)); destPath.lineTo (x2, y2); } struct LineSection { float x1, y1, x2, y2; // original line float lx1, ly1, lx2, ly2; // the left-hand stroke float rx1, ry1, rx2, ry2; // the right-hand stroke }; static void shortenSubPath (Array& subPath, float amountAtStart, float amountAtEnd) { while (amountAtEnd > 0 && subPath.size() > 0) { LineSection& l = subPath.getReference (subPath.size() - 1); float dx = l.rx2 - l.rx1; float dy = l.ry2 - l.ry1; const float len = juce_hypot (dx, dy); if (len <= amountAtEnd && subPath.size() > 1) { LineSection& prev = subPath.getReference (subPath.size() - 2); prev.x2 = l.x2; prev.y2 = l.y2; subPath.removeLast(); amountAtEnd -= len; } else { const float prop = jmin (0.9999f, amountAtEnd / len); dx *= prop; dy *= prop; l.rx1 += dx; l.ry1 += dy; l.lx2 += dx; l.ly2 += dy; break; } } while (amountAtStart > 0 && subPath.size() > 0) { LineSection& l = subPath.getReference (0); float dx = l.rx2 - l.rx1; float dy = l.ry2 - l.ry1; const float len = juce_hypot (dx, dy); if (len <= amountAtStart && subPath.size() > 1) { LineSection& next = subPath.getReference (1); next.x1 = l.x1; next.y1 = l.y1; subPath.remove (0); amountAtStart -= len; } else { const float prop = jmin (0.9999f, amountAtStart / len); dx *= prop; dy *= prop; l.rx2 -= dx; l.ry2 -= dy; l.lx1 -= dx; l.ly1 -= dy; break; } } } static void addSubPath (Path& destPath, Array& subPath, const bool isClosed, const float width, const float maxMiterExtensionSquared, const PathStrokeType::JointStyle jointStyle, const PathStrokeType::EndCapStyle endStyle, const Arrowhead* const arrowhead) { jassert (subPath.size() > 0); if (arrowhead != nullptr) shortenSubPath (subPath, arrowhead->startLength, arrowhead->endLength); const LineSection& firstLine = subPath.getReference (0); float lastX1 = firstLine.lx1; float lastY1 = firstLine.ly1; float lastX2 = firstLine.lx2; float lastY2 = firstLine.ly2; if (isClosed) { destPath.startNewSubPath (lastX1, lastY1); } else { destPath.startNewSubPath (firstLine.rx2, firstLine.ry2); if (arrowhead != nullptr) addArrowhead (destPath, firstLine.rx2, firstLine.ry2, lastX1, lastY1, firstLine.x1, firstLine.y1, width, arrowhead->startWidth); else addLineEnd (destPath, endStyle, firstLine.rx2, firstLine.ry2, lastX1, lastY1, width); } for (int i = 1; i < subPath.size(); ++i) { const LineSection& l = subPath.getReference (i); addEdgeAndJoint (destPath, jointStyle, maxMiterExtensionSquared, width, lastX1, lastY1, lastX2, lastY2, l.lx1, l.ly1, l.lx2, l.ly2, l.x1, l.y1); lastX1 = l.lx1; lastY1 = l.ly1; lastX2 = l.lx2; lastY2 = l.ly2; } const LineSection& lastLine = subPath.getReference (subPath.size() - 1); if (isClosed) { const LineSection& l = subPath.getReference (0); addEdgeAndJoint (destPath, jointStyle, maxMiterExtensionSquared, width, lastX1, lastY1, lastX2, lastY2, l.lx1, l.ly1, l.lx2, l.ly2, l.x1, l.y1); destPath.closeSubPath(); destPath.startNewSubPath (lastLine.rx1, lastLine.ry1); } else { destPath.lineTo (lastX2, lastY2); if (arrowhead != nullptr) addArrowhead (destPath, lastX2, lastY2, lastLine.rx1, lastLine.ry1, lastLine.x2, lastLine.y2, width, arrowhead->endWidth); else addLineEnd (destPath, endStyle, lastX2, lastY2, lastLine.rx1, lastLine.ry1, width); } lastX1 = lastLine.rx1; lastY1 = lastLine.ry1; lastX2 = lastLine.rx2; lastY2 = lastLine.ry2; for (int i = subPath.size() - 1; --i >= 0;) { const LineSection& l = subPath.getReference (i); addEdgeAndJoint (destPath, jointStyle, maxMiterExtensionSquared, width, lastX1, lastY1, lastX2, lastY2, l.rx1, l.ry1, l.rx2, l.ry2, l.x2, l.y2); lastX1 = l.rx1; lastY1 = l.ry1; lastX2 = l.rx2; lastY2 = l.ry2; } if (isClosed) { addEdgeAndJoint (destPath, jointStyle, maxMiterExtensionSquared, width, lastX1, lastY1, lastX2, lastY2, lastLine.rx1, lastLine.ry1, lastLine.rx2, lastLine.ry2, lastLine.x2, lastLine.y2); } else { // do the last line destPath.lineTo (lastX2, lastY2); } destPath.closeSubPath(); } static void createStroke (const float thickness, const PathStrokeType::JointStyle jointStyle, const PathStrokeType::EndCapStyle endStyle, Path& destPath, const Path& source, const AffineTransform& transform, const float extraAccuracy, const Arrowhead* const arrowhead) { jassert (extraAccuracy > 0); if (thickness <= 0) { destPath.clear(); return; } const Path* sourcePath = &source; Path temp; if (sourcePath == &destPath) { destPath.swapWithPath (temp); sourcePath = &temp; } else { destPath.clear(); } destPath.setUsingNonZeroWinding (true); const float maxMiterExtensionSquared = 9.0f * thickness * thickness; const float width = 0.5f * thickness; // Iterate the path, creating a list of the // left/right-hand lines along either side of it... PathFlatteningIterator it (*sourcePath, transform, PathFlatteningIterator::defaultTolerance / extraAccuracy); Array subPath; subPath.ensureStorageAllocated (512); LineSection l; l.x1 = 0; l.y1 = 0; const float minSegmentLength = 0.0001f; while (it.next()) { if (it.subPathIndex == 0) { if (subPath.size() > 0) { addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead); subPath.clearQuick(); } l.x1 = it.x1; l.y1 = it.y1; } l.x2 = it.x2; l.y2 = it.y2; float dx = l.x2 - l.x1; float dy = l.y2 - l.y1; const float hypotSquared = dx*dx + dy*dy; if (it.closesSubPath || hypotSquared > minSegmentLength || it.isLastInSubpath()) { const float len = std::sqrt (hypotSquared); if (len == 0) { l.rx1 = l.rx2 = l.lx1 = l.lx2 = l.x1; l.ry1 = l.ry2 = l.ly1 = l.ly2 = l.y1; } else { const float offset = width / len; dx *= offset; dy *= offset; l.rx2 = l.x1 - dy; l.ry2 = l.y1 + dx; l.lx1 = l.x1 + dy; l.ly1 = l.y1 - dx; l.lx2 = l.x2 + dy; l.ly2 = l.y2 - dx; l.rx1 = l.x2 - dy; l.ry1 = l.y2 + dx; } subPath.add (l); if (it.closesSubPath) { addSubPath (destPath, subPath, true, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead); subPath.clearQuick(); } else { l.x1 = it.x2; l.y1 = it.y2; } } } if (subPath.size() > 0) addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead); } } void PathStrokeType::createStrokedPath (Path& destPath, const Path& sourcePath, const AffineTransform& transform, const float extraAccuracy) const { PathStrokeHelpers::createStroke (thickness, jointStyle, endStyle, destPath, sourcePath, transform, extraAccuracy, 0); } void PathStrokeType::createDashedStroke (Path& destPath, const Path& sourcePath, const float* dashLengths, int numDashLengths, const AffineTransform& transform, const float extraAccuracy) const { jassert (extraAccuracy > 0); if (thickness <= 0) return; // this should really be an even number.. jassert ((numDashLengths & 1) == 0); Path newDestPath; PathFlatteningIterator it (sourcePath, transform, PathFlatteningIterator::defaultTolerance / extraAccuracy); bool first = true; int dashNum = 0; float pos = 0.0f, lineLen = 0.0f, lineEndPos = 0.0f; float dx = 0.0f, dy = 0.0f; for (;;) { const bool isSolid = ((dashNum & 1) == 0); const float dashLen = dashLengths [dashNum++ % numDashLengths]; jassert (dashLen > 0); // must be a positive increment! if (dashLen <= 0) break; pos += dashLen; while (pos > lineEndPos) { if (! it.next()) { if (isSolid && ! first) newDestPath.lineTo (it.x2, it.y2); createStrokedPath (destPath, newDestPath, AffineTransform::identity, extraAccuracy); return; } if (isSolid && ! first) newDestPath.lineTo (it.x1, it.y1); else newDestPath.startNewSubPath (it.x1, it.y1); dx = it.x2 - it.x1; dy = it.y2 - it.y1; lineLen = juce_hypot (dx, dy); lineEndPos += lineLen; first = it.closesSubPath; } const float alpha = (pos - (lineEndPos - lineLen)) / lineLen; if (isSolid) newDestPath.lineTo (it.x1 + dx * alpha, it.y1 + dy * alpha); else newDestPath.startNewSubPath (it.x1 + dx * alpha, it.y1 + dy * alpha); } } void PathStrokeType::createStrokeWithArrowheads (Path& destPath, const Path& sourcePath, const float arrowheadStartWidth, const float arrowheadStartLength, const float arrowheadEndWidth, const float arrowheadEndLength, const AffineTransform& transform, const float extraAccuracy) const { PathStrokeHelpers::Arrowhead head; head.startWidth = arrowheadStartWidth; head.startLength = arrowheadStartLength; head.endWidth = arrowheadEndWidth; head.endLength = arrowheadEndLength; PathStrokeHelpers::createStroke (thickness, jointStyle, endStyle, destPath, sourcePath, transform, extraAccuracy, &head); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.h000066400000000000000000000251541320201440200323610ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_PATHSTROKETYPE_H_INCLUDED #define JUCE_PATHSTROKETYPE_H_INCLUDED //============================================================================== /** Describes a type of stroke used to render a solid outline along a path. A PathStrokeType object can be used directly to create the shape of an outline around a path, and is used by Graphics::strokePath to specify the type of stroke to draw. @see Path, Graphics::strokePath */ class JUCE_API PathStrokeType { public: //============================================================================== /** The type of shape to use for the corners between two adjacent line segments. */ enum JointStyle { mitered, /**< Indicates that corners should be drawn with sharp joints. Note that for angles that curve back on themselves, drawing a mitre could require extending the point too far away from the path, so a mitre limit is imposed and any corners that exceed it are drawn as bevelled instead. */ curved, /**< Indicates that corners should be drawn as rounded-off. */ beveled /**< Indicates that corners should be drawn with a line flattening their outside edge. */ }; /** The type shape to use for the ends of lines. */ enum EndCapStyle { butt, /**< Ends of lines are flat and don't extend beyond the end point. */ square, /**< Ends of lines are flat, but stick out beyond the end point for half the thickness of the stroke. */ rounded /**< Ends of lines are rounded-off with a circular shape. */ }; //============================================================================== /** Creates a stroke type with a given line-width, and default joint/end styles. */ explicit PathStrokeType (float strokeThickness) noexcept; /** Creates a stroke type. @param strokeThickness the width of the line to use @param jointStyle the type of joints to use for corners @param endStyle the type of end-caps to use for the ends of open paths. */ PathStrokeType (float strokeThickness, JointStyle jointStyle, EndCapStyle endStyle = butt) noexcept; /** Creates a copy of another stroke type. */ PathStrokeType (const PathStrokeType&) noexcept; /** Copies another stroke onto this one. */ PathStrokeType& operator= (const PathStrokeType&) noexcept; /** Destructor. */ ~PathStrokeType() noexcept; //============================================================================== /** Applies this stroke type to a path and returns the resultant stroke as another Path. @param destPath the resultant stroked outline shape will be copied into this path. Note that it's ok for the source and destination Paths to be the same object, so you can easily turn a path into a stroked version of itself. @param sourcePath the path to use as the source @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to a higher resolution, which improves the quality if you'll later want to enlarge the stroked path. So for example, if you're planning on drawing the stroke at 3x the size that you're creating it, you should set this to 3. @see createDashedStroke */ void createStrokedPath (Path& destPath, const Path& sourcePath, const AffineTransform& transform = AffineTransform::identity, float extraAccuracy = 1.0f) const; //============================================================================== /** Applies this stroke type to a path, creating a dashed line. This is similar to createStrokedPath, but uses the array passed in to break the stroke up into a series of dashes. @param destPath the resultant stroked outline shape will be copied into this path. Note that it's ok for the source and destination Paths to be the same object, so you can easily turn a path into a stroked version of itself. @param sourcePath the path to use as the source @param dashLengths An array of alternating on/off lengths. E.g. { 2, 3, 4, 5 } will create a line of length 2, then skip a length of 3, then add a line of length 4, skip 5, and keep repeating this pattern. @param numDashLengths The number of lengths in the dashLengths array. This should really be an even number, otherwise the pattern will get out of step as it repeats. @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to a higher resolution, which improves the quality if you'll later want to enlarge the stroked path. So for example, if you're planning on drawing the stroke at 3x the size that you're creating it, you should set this to 3. */ void createDashedStroke (Path& destPath, const Path& sourcePath, const float* dashLengths, int numDashLengths, const AffineTransform& transform = AffineTransform::identity, float extraAccuracy = 1.0f) const; //============================================================================== /** Applies this stroke type to a path and returns the resultant stroke as another Path. @param destPath the resultant stroked outline shape will be copied into this path. Note that it's ok for the source and destination Paths to be the same object, so you can easily turn a path into a stroked version of itself. @param sourcePath the path to use as the source @param arrowheadStartWidth the width of the arrowhead at the start of the path @param arrowheadStartLength the length of the arrowhead at the start of the path @param arrowheadEndWidth the width of the arrowhead at the end of the path @param arrowheadEndLength the length of the arrowhead at the end of the path @param transform an optional transform to apply to the points from the source path as they are being used @param extraAccuracy if this is greater than 1.0, it will subdivide the path to a higher resolution, which improves the quality if you'll later want to enlarge the stroked path. So for example, if you're planning on drawing the stroke at 3x the size that you're creating it, you should set this to 3. @see createDashedStroke */ void createStrokeWithArrowheads (Path& destPath, const Path& sourcePath, float arrowheadStartWidth, float arrowheadStartLength, float arrowheadEndWidth, float arrowheadEndLength, const AffineTransform& transform = AffineTransform::identity, float extraAccuracy = 1.0f) const; //============================================================================== /** Returns the stroke thickness. */ float getStrokeThickness() const noexcept { return thickness; } /** Sets the stroke thickness. */ void setStrokeThickness (float newThickness) noexcept { thickness = newThickness; } /** Returns the joint style. */ JointStyle getJointStyle() const noexcept { return jointStyle; } /** Sets the joint style. */ void setJointStyle (JointStyle newStyle) noexcept { jointStyle = newStyle; } /** Returns the end-cap style. */ EndCapStyle getEndStyle() const noexcept { return endStyle; } /** Sets the end-cap style. */ void setEndStyle (EndCapStyle newStyle) noexcept { endStyle = newStyle; } //============================================================================== /** Compares the stroke thickness, joint and end styles of two stroke types. */ bool operator== (const PathStrokeType&) const noexcept; /** Compares the stroke thickness, joint and end styles of two stroke types. */ bool operator!= (const PathStrokeType&) const noexcept; private: //============================================================================== float thickness; JointStyle jointStyle; EndCapStyle endStyle; JUCE_LEAK_DETECTOR (PathStrokeType) }; #endif // JUCE_PATHSTROKETYPE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h000066400000000000000000000267441320201440200305320ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_POINT_H_INCLUDED #define JUCE_POINT_H_INCLUDED //============================================================================== /** A pair of (x, y) coordinates. The ValueType template should be a primitive type such as int, float, double, rather than a class. @see Line, Path, AffineTransform */ template class Point { public: /** Creates a point at the origin */ Point() noexcept : x(), y() {} /** Creates a copy of another point. */ Point (const Point& other) noexcept : x (other.x), y (other.y) {} /** Creates a point from an (x, y) position. */ Point (ValueType initialX, ValueType initialY) noexcept : x (initialX), y (initialY) {} //============================================================================== /** Copies this point from another one. */ Point& operator= (const Point& other) noexcept { x = other.x; y = other.y; return *this; } inline bool operator== (Point other) const noexcept { return x == other.x && y == other.y; } inline bool operator!= (Point other) const noexcept { return x != other.x || y != other.y; } /** Returns true if the point is (0, 0). */ bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); } /** Returns true if the coordinates are finite values. */ inline bool isFinite() const noexcept { return juce_isfinite(x) && juce_isfinite(y); } /** Returns the point's x coordinate. */ inline ValueType getX() const noexcept { return x; } /** Returns the point's y coordinate. */ inline ValueType getY() const noexcept { return y; } /** Sets the point's x coordinate. */ inline void setX (ValueType newX) noexcept { x = newX; } /** Sets the point's y coordinate. */ inline void setY (ValueType newY) noexcept { y = newY; } /** Returns a point which has the same Y position as this one, but a new X. */ Point withX (ValueType newX) const noexcept { return Point (newX, y); } /** Returns a point which has the same X position as this one, but a new Y. */ Point withY (ValueType newY) const noexcept { return Point (x, newY); } /** Changes the point's x and y coordinates. */ void setXY (ValueType newX, ValueType newY) noexcept { x = newX; y = newY; } /** Adds a pair of coordinates to this value. */ void addXY (ValueType xToAdd, ValueType yToAdd) noexcept { x += xToAdd; y += yToAdd; } //============================================================================== /** Returns a point with a given offset from this one. */ Point translated (ValueType deltaX, ValueType deltaY) const noexcept { return Point (x + deltaX, y + deltaY); } /** Adds two points together */ Point operator+ (Point other) const noexcept { return Point (x + other.x, y + other.y); } /** Adds another point's coordinates to this one */ Point& operator+= (Point other) noexcept { x += other.x; y += other.y; return *this; } /** Subtracts one points from another */ Point operator- (Point other) const noexcept { return Point (x - other.x, y - other.y); } /** Subtracts another point's coordinates to this one */ Point& operator-= (Point other) noexcept { x -= other.x; y -= other.y; return *this; } /** Multiplies two points together */ template Point operator* (Point other) const noexcept { return Point ((ValueType) (x * other.x), (ValueType) (y * other.y)); } /** Multiplies another point's coordinates to this one */ template Point& operator*= (Point other) noexcept { *this = *this * other; return *this; } /** Divides one point by another */ template Point operator/ (Point other) const noexcept { return Point ((ValueType) (x / other.x), (ValueType) (y / other.y)); } /** Divides this point's coordinates by another */ template Point& operator/= (Point other) noexcept { *this = *this / other; return *this; } /** Returns a point whose coordinates are multiplied by a given scalar value. */ template Point operator* (FloatType multiplier) const noexcept { return Point ((ValueType) (x * multiplier), (ValueType) (y * multiplier)); } /** Returns a point whose coordinates are divided by a given scalar value. */ template Point operator/ (FloatType divisor) const noexcept { return Point ((ValueType) (x / divisor), (ValueType) (y / divisor)); } /** Multiplies the point's coordinates by a scalar value. */ template Point& operator*= (FloatType multiplier) noexcept { x = (ValueType) (x * multiplier); y = (ValueType) (y * multiplier); return *this; } /** Divides the point's coordinates by a scalar value. */ template Point& operator/= (FloatType divisor) noexcept { x = (ValueType) (x / divisor); y = (ValueType) (y / divisor); return *this; } /** Returns the inverse of this point. */ Point operator-() const noexcept { return Point (-x, -y); } //============================================================================== /** This type will be double if the Point's type is double, otherwise it will be float. */ typedef typename TypeHelpers::SmallestFloatType::type FloatType; //============================================================================== /** Returns the straight-line distance between this point and the origin. */ ValueType getDistanceFromOrigin() const noexcept { return juce_hypot (x, y); } /** Returns the straight-line distance between this point and another one. */ ValueType getDistanceFrom (Point other) const noexcept { return juce_hypot (x - other.x, y - other.y); } /** Returns the angle from this point to another one. The return value is the number of radians clockwise from the 12 o'clock direction, where this point is the centre and the other point is on the circumference. */ FloatType getAngleToPoint (Point other) const noexcept { return static_cast (std::atan2 (static_cast (other.x - x), static_cast (y - other.y))); } /** Returns the point that would be reached by rotating this point clockwise about the origin by the specified angle. */ Point rotatedAboutOrigin (ValueType angleRadians) const noexcept { return Point (x * std::cos (angleRadians) - y * std::sin (angleRadians), x * std::sin (angleRadians) + y * std::cos (angleRadians)); } /** Taking this point to be the centre of a circle, this returns a point on its circumference. @param radius the radius of the circle. @param angle the angle of the point, in radians clockwise from the 12 o'clock position. */ Point getPointOnCircumference (float radius, float angle) const noexcept { return Point (static_cast (x + radius * std::sin (angle)), static_cast (y - radius * std::cos (angle))); } /** Taking this point to be the centre of an ellipse, this returns a point on its circumference. @param radiusX the horizontal radius of the circle. @param radiusY the vertical radius of the circle. @param angle the angle of the point, in radians clockwise from the 12 o'clock position. */ Point getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept { return Point (static_cast (x + radiusX * std::sin (angle)), static_cast (y - radiusY * std::cos (angle))); } /** Returns the dot-product of two points (x1 * x2 + y1 * y2). */ FloatType getDotProduct (Point other) const noexcept { return x * other.x + y * other.y; } //============================================================================== /** Uses a transform to change the point's coordinates. This will only compile if ValueType = float! @see AffineTransform::transformPoint */ void applyTransform (const AffineTransform& transform) noexcept { transform.transformPoint (x, y); } /** Returns the position of this point, if it is transformed by a given AffineTransform. */ Point transformedBy (const AffineTransform& transform) const noexcept { return Point (static_cast (transform.mat00 * x + transform.mat01 * y + transform.mat02), static_cast (transform.mat10 * x + transform.mat11 * y + transform.mat12)); } //============================================================================== /** Casts this point to a Point object. */ Point toInt() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Casts this point to a Point object. */ Point toFloat() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Casts this point to a Point object. */ Point toDouble() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Casts this point to a Point object using roundToInt() to convert the values. */ Point roundToInt() const noexcept { return Point (juce::roundToInt (x), juce::roundToInt (y)); } /** Returns the point as a string in the form "x, y". */ String toString() const { return String (x) + ", " + String (y); } //============================================================================== ValueType x; /**< The point's X coordinate. */ ValueType y; /**< The point's Y coordinate. */ }; #endif // JUCE_POINT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h000066400000000000000000001234441320201440200313400ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_RECTANGLE_H_INCLUDED #define JUCE_RECTANGLE_H_INCLUDED //============================================================================== /** Manages a rectangle and allows geometric operations to be performed on it. @see RectangleList, Path, Line, Point */ template class Rectangle { public: //============================================================================== /** Creates a rectangle of zero size. The default coordinates will be (0, 0, 0, 0). */ Rectangle() noexcept : w(), h() { } /** Creates a copy of another rectangle. */ Rectangle (const Rectangle& other) noexcept : pos (other.pos), w (other.w), h (other.h) { } /** Creates a rectangle with a given position and size. */ Rectangle (ValueType initialX, ValueType initialY, ValueType width, ValueType height) noexcept : pos (initialX, initialY), w (width), h (height) { } /** Creates a rectangle with a given size, and a position of (0, 0). */ Rectangle (ValueType width, ValueType height) noexcept : w (width), h (height) { } /** Creates a Rectangle from the positions of two opposite corners. */ Rectangle (Point corner1, Point corner2) noexcept : pos (jmin (corner1.x, corner2.x), jmin (corner1.y, corner2.y)), w (corner1.x - corner2.x), h (corner1.y - corner2.y) { if (w < ValueType()) w = -w; if (h < ValueType()) h = -h; } /** Creates a Rectangle from a set of left, right, top, bottom coordinates. The right and bottom values must be larger than the left and top ones, or the resulting rectangle will have a negative size. */ static Rectangle leftTopRightBottom (ValueType left, ValueType top, ValueType right, ValueType bottom) noexcept { return Rectangle (left, top, right - left, bottom - top); } Rectangle& operator= (const Rectangle& other) noexcept { pos = other.pos; w = other.w; h = other.h; return *this; } /** Destructor. */ ~Rectangle() noexcept {} //============================================================================== /** Returns true if the rectangle's width or height are zero or less */ bool isEmpty() const noexcept { return w <= ValueType() || h <= ValueType(); } /** Returns true if the rectangle's values are all finite numbers, i.e. not NaN or infinity. */ inline bool isFinite() const noexcept { return pos.isFinite() && juce_isfinite(w) && juce_isfinite(h); } /** Returns the x coordinate of the rectangle's left-hand-side. */ inline ValueType getX() const noexcept { return pos.x; } /** Returns the y coordinate of the rectangle's top edge. */ inline ValueType getY() const noexcept { return pos.y; } /** Returns the width of the rectangle. */ inline ValueType getWidth() const noexcept { return w; } /** Returns the height of the rectangle. */ inline ValueType getHeight() const noexcept { return h; } /** Returns the x coordinate of the rectangle's right-hand-side. */ inline ValueType getRight() const noexcept { return pos.x + w; } /** Returns the y coordinate of the rectangle's bottom edge. */ inline ValueType getBottom() const noexcept { return pos.y + h; } /** Returns the x coordinate of the rectangle's centre. */ ValueType getCentreX() const noexcept { return pos.x + w / (ValueType) 2; } /** Returns the y coordinate of the rectangle's centre. */ ValueType getCentreY() const noexcept { return pos.y + h / (ValueType) 2; } /** Returns the centre point of the rectangle. */ Point getCentre() const noexcept { return Point (pos.x + w / (ValueType) 2, pos.y + h / (ValueType) 2); } /** Returns the aspect ratio of the rectangle's width / height. If widthOverHeight is true, it returns width / height; if widthOverHeight is false, it returns height / width. */ ValueType getAspectRatio (bool widthOverHeight = true) const noexcept { return widthOverHeight ? w / h : h / w; } //============================================================================== /** Returns the rectangle's top-left position as a Point. */ inline Point getPosition() const noexcept { return pos; } /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ inline void setPosition (Point newPos) noexcept { pos = newPos; } /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ inline void setPosition (ValueType newX, ValueType newY) noexcept { pos.setXY (newX, newY); } /** Returns the rectangle's top-left position as a Point. */ Point getTopLeft() const noexcept { return pos; } /** Returns the rectangle's top-right position as a Point. */ Point getTopRight() const noexcept { return Point (pos.x + w, pos.y); } /** Returns the rectangle's bottom-left position as a Point. */ Point getBottomLeft() const noexcept { return Point (pos.x, pos.y + h); } /** Returns the rectangle's bottom-right position as a Point. */ Point getBottomRight() const noexcept { return Point (pos.x + w, pos.y + h); } /** Returns the rectangle's left and right positions as a Range. */ Range getHorizontalRange() const noexcept { return Range::withStartAndLength (pos.x, w); } /** Returns the rectangle's top and bottom positions as a Range. */ Range getVerticalRange() const noexcept { return Range::withStartAndLength (pos.y, h); } /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */ void setSize (ValueType newWidth, ValueType newHeight) noexcept { w = newWidth; h = newHeight; } /** Changes all the rectangle's coordinates. */ void setBounds (ValueType newX, ValueType newY, ValueType newWidth, ValueType newHeight) noexcept { pos.x = newX; pos.y = newY; w = newWidth; h = newHeight; } /** Changes the rectangle's X coordinate */ inline void setX (ValueType newX) noexcept { pos.x = newX; } /** Changes the rectangle's Y coordinate */ inline void setY (ValueType newY) noexcept { pos.y = newY; } /** Changes the rectangle's width */ inline void setWidth (ValueType newWidth) noexcept { w = newWidth; } /** Changes the rectangle's height */ inline void setHeight (ValueType newHeight) noexcept { h = newHeight; } /** Changes the position of the rectangle's centre (leaving its size unchanged). */ inline void setCentre (ValueType newCentreX, ValueType newCentreY) noexcept { pos.x = newCentreX - w / (ValueType) 2; pos.y = newCentreY - h / (ValueType) 2; } /** Changes the position of the rectangle's centre (leaving its size unchanged). */ inline void setCentre (Point newCentre) noexcept { setCentre (newCentre.x, newCentre.y); } /** Changes the position of the rectangle's left and right edges. */ void setHorizontalRange (Range range) noexcept { pos.x = range.getStart(); w = range.getLength(); } /** Changes the position of the rectangle's top and bottom edges. */ void setVerticalRange (Range range) noexcept { pos.y = range.getStart(); h = range.getLength(); } /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ Rectangle withX (ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); } /** Returns a rectangle which has the same size and x-position as this one, but with a different y-position. */ Rectangle withY (ValueType newY) const noexcept { return Rectangle (pos.x, newY, w, h); } /** Returns a rectangle with the same size as this one, but a new position. */ Rectangle withPosition (ValueType newX, ValueType newY) const noexcept { return Rectangle (newX, newY, w, h); } /** Returns a rectangle with the same size as this one, but a new position. */ Rectangle withPosition (Point newPos) const noexcept { return Rectangle (newPos.x, newPos.y, w, h); } /** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */ Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } /** Returns a rectangle with the same size as this one, but a new centre position. */ Rectangle withCentre (Point newCentre) const noexcept { return Rectangle (newCentre.x - w / (ValueType) 2, newCentre.y - h / (ValueType) 2, w, h); } /** Returns a rectangle which has the same position and height as this one, but with a different width. */ Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } /** Returns a rectangle which has the same position and width as this one, but with a different height. */ Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } /** Returns a rectangle with the same top-left position as this one, but a new size. */ Rectangle withSize (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } /** Returns a rectangle with the same centre position as this one, but a new size. */ Rectangle withSizeKeepingCentre (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x + (w - newWidth) / (ValueType) 2, pos.y + (h - newHeight) / (ValueType) 2, newWidth, newHeight); } /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. @see withLeft */ void setLeft (ValueType newLeft) noexcept { w = jmax (ValueType(), pos.x + w - newLeft); pos.x = newLeft; } /** Returns a new rectangle with a different x position, but the same right-hand edge as this one. If the new x is beyond the right of the current right-hand edge, the width will be set to zero. @see setLeft */ Rectangle withLeft (ValueType newLeft) const noexcept { return Rectangle (newLeft, pos.y, jmax (ValueType(), pos.x + w - newLeft), h); } /** Moves the y position, adjusting the height so that the bottom edge remains in the same place. If the y is moved to be below the current bottom edge, the height will be set to zero. @see withTop */ void setTop (ValueType newTop) noexcept { h = jmax (ValueType(), pos.y + h - newTop); pos.y = newTop; } /** Returns a new rectangle with a different y position, but the same bottom edge as this one. If the new y is beyond the bottom of the current rectangle, the height will be set to zero. @see setTop */ Rectangle withTop (ValueType newTop) const noexcept { return Rectangle (pos.x, newTop, w, jmax (ValueType(), pos.y + h - newTop)); } /** Adjusts the width so that the right-hand edge of the rectangle has this new value. If the new right is below the current X value, the X will be pushed down to match it. @see getRight, withRight */ void setRight (ValueType newRight) noexcept { pos.x = jmin (pos.x, newRight); w = newRight - pos.x; } /** Returns a new rectangle with a different right-hand edge position, but the same left-hand edge as this one. If the new right edge is below the current left-hand edge, the width will be set to zero. @see setRight */ Rectangle withRight (ValueType newRight) const noexcept { return Rectangle (jmin (pos.x, newRight), pos.y, jmax (ValueType(), newRight - pos.x), h); } /** Adjusts the height so that the bottom edge of the rectangle has this new value. If the new bottom is lower than the current Y value, the Y will be pushed down to match it. @see getBottom, withBottom */ void setBottom (ValueType newBottom) noexcept { pos.y = jmin (pos.y, newBottom); h = newBottom - pos.y; } /** Returns a new rectangle with a different bottom edge position, but the same top edge as this one. If the new y is beyond the bottom of the current rectangle, the height will be set to zero. @see setBottom */ Rectangle withBottom (ValueType newBottom) const noexcept { return Rectangle (pos.x, jmin (pos.y, newBottom), w, jmax (ValueType(), newBottom - pos.y)); } /** Returns a version of this rectangle with the given amount removed from its left-hand edge. */ Rectangle withTrimmedLeft (ValueType amountToRemove) const noexcept { return withLeft (pos.x + amountToRemove); } /** Returns a version of this rectangle with the given amount removed from its right-hand edge. */ Rectangle withTrimmedRight (ValueType amountToRemove) const noexcept { return withWidth (w - amountToRemove); } /** Returns a version of this rectangle with the given amount removed from its top edge. */ Rectangle withTrimmedTop (ValueType amountToRemove) const noexcept { return withTop (pos.y + amountToRemove); } /** Returns a version of this rectangle with the given amount removed from its bottom edge. */ Rectangle withTrimmedBottom (ValueType amountToRemove) const noexcept { return withHeight (h - amountToRemove); } //============================================================================== /** Moves the rectangle's position by adding amount to its x and y coordinates. */ void translate (ValueType deltaX, ValueType deltaY) noexcept { pos.x += deltaX; pos.y += deltaY; } /** Returns a rectangle which is the same as this one moved by a given amount. */ Rectangle translated (ValueType deltaX, ValueType deltaY) const noexcept { return Rectangle (pos.x + deltaX, pos.y + deltaY, w, h); } /** Returns a rectangle which is the same as this one moved by a given amount. */ Rectangle operator+ (Point deltaPosition) const noexcept { return Rectangle (pos.x + deltaPosition.x, pos.y + deltaPosition.y, w, h); } /** Moves this rectangle by a given amount. */ Rectangle& operator+= (Point deltaPosition) noexcept { pos += deltaPosition; return *this; } /** Returns a rectangle which is the same as this one moved by a given amount. */ Rectangle operator- (Point deltaPosition) const noexcept { return Rectangle (pos.x - deltaPosition.x, pos.y - deltaPosition.y, w, h); } /** Moves this rectangle by a given amount. */ Rectangle& operator-= (Point deltaPosition) noexcept { pos -= deltaPosition; return *this; } /** Returns a rectangle that has been scaled by the given amount, centred around the origin. Note that if the rectangle has int coordinates and it's scaled by a floating-point amount, then the result will be converted back to integer coordinates using getSmallestIntegerContainer(). */ template Rectangle operator* (FloatType scaleFactor) const noexcept { Rectangle r (*this); r *= scaleFactor; return r; } /** Scales this rectangle by the given amount, centred around the origin. Note that if the rectangle has int coordinates and it's scaled by a floating-point amount, then the result will be converted back to integer coordinates using getSmallestIntegerContainer(). */ template Rectangle operator*= (FloatType scaleFactor) noexcept { Rectangle (pos.x * scaleFactor, pos.y * scaleFactor, w * scaleFactor, h * scaleFactor).copyWithRounding (*this); return *this; } /** Scales this rectangle by the given X and Y factors, centred around the origin. Note that if the rectangle has int coordinates and it's scaled by a floating-point amount, then the result will be converted back to integer coordinates using getSmallestIntegerContainer(). */ template Rectangle operator*= (Point scaleFactor) noexcept { Rectangle (pos.x * scaleFactor.x, pos.y * scaleFactor.y, w * scaleFactor.x, h * scaleFactor.y).copyWithRounding (*this); return *this; } /** Scales this rectangle by the given amount, centred around the origin. */ template Rectangle operator/ (FloatType scaleFactor) const noexcept { Rectangle r (*this); r /= scaleFactor; return r; } /** Scales this rectangle by the given amount, centred around the origin. */ template Rectangle operator/= (FloatType scaleFactor) noexcept { Rectangle (pos.x / scaleFactor, pos.y / scaleFactor, w / scaleFactor, h / scaleFactor).copyWithRounding (*this); return *this; } /** Scales this rectangle by the given X and Y factors, centred around the origin. */ template Rectangle operator/= (Point scaleFactor) noexcept { Rectangle (pos.x / scaleFactor.x, pos.y / scaleFactor.y, w / scaleFactor.x, h / scaleFactor.y).copyWithRounding (*this); return *this; } /** Expands the rectangle by a given amount. Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). @see expanded, reduce, reduced */ void expand (ValueType deltaX, ValueType deltaY) noexcept { const ValueType nw = jmax (ValueType(), w + deltaX * 2); const ValueType nh = jmax (ValueType(), h + deltaY * 2); setBounds (pos.x - deltaX, pos.y - deltaY, nw, nh); } /** Returns a rectangle that is larger than this one by a given amount. Effectively, the rectangle returned is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). @see expand, reduce, reduced */ Rectangle expanded (ValueType deltaX, ValueType deltaY) const noexcept { const ValueType nw = jmax (ValueType(), w + deltaX * 2); const ValueType nh = jmax (ValueType(), h + deltaY * 2); return Rectangle (pos.x - deltaX, pos.y - deltaY, nw, nh); } /** Returns a rectangle that is larger than this one by a given amount. Effectively, the rectangle returned is (x - delta, y - delta, w + delta * 2, h + delta * 2). @see expand, reduce, reduced */ Rectangle expanded (ValueType delta) const noexcept { return expanded (delta, delta); } /** Shrinks the rectangle by a given amount. Effectively, its new size is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2). @see reduced, expand, expanded */ void reduce (ValueType deltaX, ValueType deltaY) noexcept { expand (-deltaX, -deltaY); } /** Returns a rectangle that is smaller than this one by a given amount. Effectively, the rectangle returned is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2). @see reduce, expand, expanded */ Rectangle reduced (ValueType deltaX, ValueType deltaY) const noexcept { return expanded (-deltaX, -deltaY); } /** Returns a rectangle that is smaller than this one by a given amount. Effectively, the rectangle returned is (x + delta, y + delta, w - delta * 2, h - delta * 2). @see reduce, expand, expanded */ Rectangle reduced (ValueType delta) const noexcept { return reduced (delta, delta); } /** Removes a strip from the top of this rectangle, reducing this rectangle by the specified amount and returning the section that was removed. E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will return (100, 100, 300, 50) and leave this rectangle as (100, 150, 300, 250). If amountToRemove is greater than the height of this rectangle, it'll be clipped to that value. */ Rectangle removeFromTop (ValueType amountToRemove) noexcept { const Rectangle r (pos.x, pos.y, w, jmin (amountToRemove, h)); pos.y += r.h; h -= r.h; return r; } /** Removes a strip from the left-hand edge of this rectangle, reducing this rectangle by the specified amount and returning the section that was removed. E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will return (100, 100, 50, 300) and leave this rectangle as (150, 100, 250, 300). If amountToRemove is greater than the width of this rectangle, it'll be clipped to that value. */ Rectangle removeFromLeft (ValueType amountToRemove) noexcept { const Rectangle r (pos.x, pos.y, jmin (amountToRemove, w), h); pos.x += r.w; w -= r.w; return r; } /** Removes a strip from the right-hand edge of this rectangle, reducing this rectangle by the specified amount and returning the section that was removed. E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will return (250, 100, 50, 300) and leave this rectangle as (100, 100, 250, 300). If amountToRemove is greater than the width of this rectangle, it'll be clipped to that value. */ Rectangle removeFromRight (ValueType amountToRemove) noexcept { amountToRemove = jmin (amountToRemove, w); const Rectangle r (pos.x + w - amountToRemove, pos.y, amountToRemove, h); w -= amountToRemove; return r; } /** Removes a strip from the bottom of this rectangle, reducing this rectangle by the specified amount and returning the section that was removed. E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will return (100, 250, 300, 50) and leave this rectangle as (100, 100, 300, 250). If amountToRemove is greater than the height of this rectangle, it'll be clipped to that value. */ Rectangle removeFromBottom (ValueType amountToRemove) noexcept { amountToRemove = jmin (amountToRemove, h); const Rectangle r (pos.x, pos.y + h - amountToRemove, w, amountToRemove); h -= amountToRemove; return r; } //============================================================================== /** Returns true if the two rectangles are identical. */ bool operator== (const Rectangle& other) const noexcept { return pos == other.pos && w == other.w && h == other.h; } /** Returns true if the two rectangles are not identical. */ bool operator!= (const Rectangle& other) const noexcept { return pos != other.pos || w != other.w || h != other.h; } /** Returns true if this coordinate is inside the rectangle. */ bool contains (ValueType xCoord, ValueType yCoord) const noexcept { return xCoord >= pos.x && yCoord >= pos.y && xCoord < pos.x + w && yCoord < pos.y + h; } /** Returns true if this coordinate is inside the rectangle. */ bool contains (Point point) const noexcept { return point.x >= pos.x && point.y >= pos.y && point.x < pos.x + w && point.y < pos.y + h; } /** Returns true if this other rectangle is completely inside this one. */ bool contains (const Rectangle& other) const noexcept { return pos.x <= other.pos.x && pos.y <= other.pos.y && pos.x + w >= other.pos.x + other.w && pos.y + h >= other.pos.y + other.h; } /** Returns the nearest point to the specified point that lies within this rectangle. */ Point getConstrainedPoint (Point point) const noexcept { return Point (jlimit (pos.x, pos.x + w, point.x), jlimit (pos.y, pos.y + h, point.y)); } /** Returns a point within this rectangle, specified as proportional coordinates. The relative X and Y values should be between 0 and 1, where 0 is the left or top of this rectangle, and 1 is the right or bottom. (Out-of-bounds values will return a point outside the rectangle). */ Point getRelativePoint (double relativeX, double relativeY) const noexcept { return Point (pos.x + static_cast (w * relativeX), pos.y + static_cast (h * relativeY)); } /** Returns true if any part of another rectangle overlaps this one. */ bool intersects (const Rectangle& other) const noexcept { return pos.x + w > other.pos.x && pos.y + h > other.pos.y && pos.x < other.pos.x + other.w && pos.y < other.pos.y + other.h && w > ValueType() && h > ValueType() && other.w > ValueType() && other.h > ValueType(); } /** Returns true if any part of the given line lies inside this rectangle. */ bool intersects (const Line& line) const noexcept { return contains (line.getStart()) || contains (line.getEnd()) || line.intersects (Line (getTopLeft(), getTopRight())) || line.intersects (Line (getTopRight(), getBottomRight())) || line.intersects (Line (getBottomRight(), getBottomLeft())) || line.intersects (Line (getBottomLeft(), getTopLeft())); } /** Returns the region that is the overlap between this and another rectangle. If the two rectangles don't overlap, the rectangle returned will be empty. */ Rectangle getIntersection (const Rectangle& other) const noexcept { const ValueType nx = jmax (pos.x, other.pos.x); const ValueType ny = jmax (pos.y, other.pos.y); const ValueType nw = jmin (pos.x + w, other.pos.x + other.w) - nx; const ValueType nh = jmin (pos.y + h, other.pos.y + other.h) - ny; if (nw >= ValueType() && nh >= ValueType()) return Rectangle (nx, ny, nw, nh); return Rectangle(); } /** Clips a set of rectangle coordinates so that they lie only within this one. This is a non-static version of intersectRectangles(). Returns false if the two rectangles didn't overlap. */ bool intersectRectangle (ValueType& otherX, ValueType& otherY, ValueType& otherW, ValueType& otherH) const noexcept { const ValueType maxX (jmax (otherX, pos.x)); otherW = jmin (otherX + otherW, pos.x + w) - maxX; if (otherW > ValueType()) { const ValueType maxY (jmax (otherY, pos.y)); otherH = jmin (otherY + otherH, pos.y + h) - maxY; if (otherH > ValueType()) { otherX = maxX; otherY = maxY; return true; } } return false; } /** Clips a rectangle so that it lies only within this one. Returns false if the two rectangles didn't overlap. */ bool intersectRectangle (Rectangle& rectangleToClip) const noexcept { return intersectRectangle (rectangleToClip.pos.x, rectangleToClip.pos.y, rectangleToClip.w, rectangleToClip.h); } /** Returns the smallest rectangle that contains both this one and the one passed-in. If either this or the other rectangle are empty, they will not be counted as part of the resulting region. */ Rectangle getUnion (const Rectangle& other) const noexcept { if (other.isEmpty()) return *this; if (isEmpty()) return other; const ValueType newX = jmin (pos.x, other.pos.x); const ValueType newY = jmin (pos.y, other.pos.y); return Rectangle (newX, newY, jmax (pos.x + w, other.pos.x + other.w) - newX, jmax (pos.y + h, other.pos.y + other.h) - newY); } /** If this rectangle merged with another one results in a simple rectangle, this will set this rectangle to the result, and return true. Returns false and does nothing to this rectangle if the two rectangles don't overlap, or if they form a complex region. */ bool enlargeIfAdjacent (const Rectangle& other) noexcept { if (pos.x == other.pos.x && getRight() == other.getRight() && (other.getBottom() >= pos.y && other.pos.y <= getBottom())) { const ValueType newY = jmin (pos.y, other.pos.y); h = jmax (getBottom(), other.getBottom()) - newY; pos.y = newY; return true; } if (pos.y == other.pos.y && getBottom() == other.getBottom() && (other.getRight() >= pos.x && other.pos.x <= getRight())) { const ValueType newX = jmin (pos.x, other.pos.x); w = jmax (getRight(), other.getRight()) - newX; pos.x = newX; return true; } return false; } /** If after removing another rectangle from this one the result is a simple rectangle, this will set this object's bounds to be the result, and return true. Returns false and does nothing to this rectangle if the two rectangles don't overlap, or if removing the other one would form a complex region. */ bool reduceIfPartlyContainedIn (const Rectangle& other) noexcept { int inside = 0; const ValueType otherR (other.getRight()); if (pos.x >= other.pos.x && pos.x < otherR) inside = 1; const ValueType otherB (other.getBottom()); if (pos.y >= other.pos.y && pos.y < otherB) inside |= 2; const ValueType r (pos.x + w); if (r >= other.pos.x && r < otherR) inside |= 4; const ValueType b (pos.y + h); if (b >= other.pos.y && b < otherB) inside |= 8; switch (inside) { case 1 + 2 + 8: w = r - otherR; pos.x = otherR; return true; case 1 + 2 + 4: h = b - otherB; pos.y = otherB; return true; case 2 + 4 + 8: w = other.pos.x - pos.x; return true; case 1 + 4 + 8: h = other.pos.y - pos.y; return true; } return false; } /** Tries to fit this rectangle within a target area, returning the result. If this rectangle is not completely inside the target area, then it'll be shifted (without changing its size) so that it lies within the target. If it is larger than the target rectangle in either dimension, then that dimension will be reduced to fit within the target. */ Rectangle constrainedWithin (const Rectangle& areaToFitWithin) const noexcept { const ValueType newW (jmin (w, areaToFitWithin.getWidth())); const ValueType newH (jmin (h, areaToFitWithin.getHeight())); return Rectangle (jlimit (areaToFitWithin.getX(), areaToFitWithin.getRight() - newW, pos.x), jlimit (areaToFitWithin.getY(), areaToFitWithin.getBottom() - newH, pos.y), newW, newH); } /** Returns the smallest rectangle that can contain the shape created by applying a transform to this rectangle. This should only be used on floating point rectangles. */ Rectangle transformedBy (const AffineTransform& transform) const noexcept { typedef typename TypeHelpers::SmallestFloatType::type FloatType; FloatType x1 = static_cast (pos.x), y1 = static_cast (pos.y); FloatType x2 = static_cast (pos.x + w), y2 = static_cast (pos.y); FloatType x3 = static_cast (pos.x), y3 = static_cast (pos.y + h); FloatType x4 = static_cast (x2), y4 = static_cast (y3); transform.transformPoints (x1, y1, x2, y2); transform.transformPoints (x3, y3, x4, y4); const FloatType rx1 = jmin (x1, x2, x3, x4); const FloatType rx2 = jmax (x1, x2, x3, x4); const FloatType ry1 = jmin (y1, y2, y3, y4); const FloatType ry2 = jmax (y1, y2, y3, y4); Rectangle r; Rectangle (rx1, ry1, rx2 - rx1, ry2 - ry1).copyWithRounding (r); return r; } /** Returns the smallest integer-aligned rectangle that completely contains this one. This is only relevent for floating-point rectangles, of course. @see toFloat() */ Rectangle getSmallestIntegerContainer() const noexcept { const int x1 = floorAsInt (pos.x); const int y1 = floorAsInt (pos.y); const int x2 = ceilAsInt (pos.x + w); const int y2 = ceilAsInt (pos.y + h); return Rectangle (x1, y1, x2 - x1, y2 - y1); } /** Casts this rectangle to a Rectangle. @see getSmallestIntegerContainer */ Rectangle toFloat() const noexcept { return Rectangle (static_cast (pos.x), static_cast (pos.y), static_cast (w), static_cast (h)); } /** Casts this rectangle to a Rectangle. @see getSmallestIntegerContainer */ Rectangle toDouble() const noexcept { return Rectangle (static_cast (pos.x), static_cast (pos.y), static_cast (w), static_cast (h)); } /** Casts this rectangle to a Rectangle with the given type. If the target type is a conversion from float to int, then the conversion will be done using getSmallestIntegerContainer(). */ template Rectangle toType() const noexcept { Rectangle r; copyWithRounding (r); return r; } /** Returns the smallest Rectangle that can contain a set of points. */ static Rectangle findAreaContainingPoints (const Point* const points, const int numPoints) noexcept { if (numPoints == 0) return Rectangle(); ValueType minX (points[0].x); ValueType maxX (minX); ValueType minY (points[0].y); ValueType maxY (minY); for (int i = 1; i < numPoints; ++i) { minX = jmin (minX, points[i].x); maxX = jmax (maxX, points[i].x); minY = jmin (minY, points[i].y); maxY = jmax (maxY, points[i].y); } return Rectangle (minX, minY, maxX - minX, maxY - minY); } //============================================================================== /** Static utility to intersect two sets of rectangular coordinates. Returns false if the two regions didn't overlap. @see intersectRectangle */ static bool intersectRectangles (ValueType& x1, ValueType& y1, ValueType& w1, ValueType& h1, const ValueType x2, const ValueType y2, const ValueType w2, const ValueType h2) noexcept { const ValueType x (jmax (x1, x2)); w1 = jmin (x1 + w1, x2 + w2) - x; if (w1 > ValueType()) { const ValueType y (jmax (y1, y2)); h1 = jmin (y1 + h1, y2 + h2) - y; if (h1 > ValueType()) { x1 = x; y1 = y; return true; } } return false; } //============================================================================== /** Creates a string describing this rectangle. The string will be of the form "x y width height", e.g. "100 100 400 200". Coupled with the fromString() method, this is very handy for things like storing rectangles (particularly component positions) in XML attributes. @see fromString */ String toString() const { String s; s.preallocateBytes (32); s << pos.x << ' ' << pos.y << ' ' << w << ' ' << h; return s; } /** Parses a string containing a rectangle's details. The string should contain 4 integer tokens, in the form "x y width height". They can be comma or whitespace separated. This method is intended to go with the toString() method, to form an easy way of saving/loading rectangles as strings. @see toString */ static Rectangle fromString (StringRef stringVersion) { StringArray toks; toks.addTokens (stringVersion.text.findEndOfWhitespace(), ",; \t\r\n", ""); return Rectangle (parseIntAfterSpace (toks[0]), parseIntAfterSpace (toks[1]), parseIntAfterSpace (toks[2]), parseIntAfterSpace (toks[3])); } #ifndef DOXYGEN // This has been renamed by transformedBy, in order to match the method names used in the Point class. JUCE_DEPRECATED_WITH_BODY (Rectangle transformed (const AffineTransform& t) const noexcept, { return transformedBy (t); }) #endif private: template friend class Rectangle; Point pos; ValueType w, h; static int parseIntAfterSpace (StringRef s) noexcept { return s.text.findEndOfWhitespace().getIntValue32(); } void copyWithRounding (Rectangle& result) const noexcept { result = getSmallestIntegerContainer(); } void copyWithRounding (Rectangle& result) const noexcept { result = toFloat(); } void copyWithRounding (Rectangle& result) const noexcept { result = toDouble(); } static int floorAsInt (int n) noexcept { return n; } static int floorAsInt (float n) noexcept { return (int) std::floor (n); } static int floorAsInt (double n) noexcept { return (int) std::floor (n); } static int ceilAsInt (int n) noexcept { return n; } static int ceilAsInt (float n) noexcept { return (int) std::ceil (n); } static int ceilAsInt (double n) noexcept { return (int) std::ceil (n); } }; #endif // JUCE_RECTANGLE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.h000066400000000000000000000536271320201440200322010ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_RECTANGLELIST_H_INCLUDED #define JUCE_RECTANGLELIST_H_INCLUDED //============================================================================== /** Maintains a set of rectangles as a complex region. This class allows a set of rectangles to be treated as a solid shape, and can add and remove rectangular sections of it, and simplify overlapping or adjacent rectangles. @see Rectangle */ template class RectangleList { public: typedef Rectangle RectangleType; //============================================================================== /** Creates an empty RectangleList */ RectangleList() noexcept {} /** Creates a copy of another list */ RectangleList (const RectangleList& other) : rects (other.rects) { } /** Creates a list containing just one rectangle. */ RectangleList (const RectangleType& rect) { addWithoutMerging (rect); } /** Copies this list from another one. */ RectangleList& operator= (const RectangleList& other) { rects = other.rects; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS RectangleList (RectangleList&& other) noexcept : rects (static_cast&&> (other.rects)) { } RectangleList& operator= (RectangleList&& other) noexcept { rects = static_cast&&> (other.rects); return *this; } #endif //============================================================================== /** Returns true if the region is empty. */ bool isEmpty() const noexcept { return rects.size() == 0; } /** Returns the number of rectangles in the list. */ int getNumRectangles() const noexcept { return rects.size(); } /** Returns one of the rectangles at a particular index. @returns the rectangle at the index, or an empty rectangle if the index is out-of-range. */ RectangleType getRectangle (int index) const noexcept { return rects[index]; } //============================================================================== /** Removes all rectangles to leave an empty region. */ void clear() { rects.clearQuick(); } /** Merges a new rectangle into the list. The rectangle being added will first be clipped to remove any parts of it that overlap existing rectangles in the list, and adjacent rectangles will be merged into it. The rectangle can have any size and may be empty, but if it's floating point then it's expected to not contain any INF values. */ void add (const RectangleType& rect) { jassert (rect.isFinite()); // You must provide a valid rectangle to this method! if (! rect.isEmpty()) { if (rects.size() == 0) { rects.add (rect); } else { bool anyOverlaps = false; for (int j = rects.size(); --j >= 0;) { RectangleType& ourRect = rects.getReference (j); if (rect.intersects (ourRect)) { if (rect.contains (ourRect)) rects.remove (j); else if (! ourRect.reduceIfPartlyContainedIn (rect)) anyOverlaps = true; } } if (anyOverlaps && rects.size() > 0) { RectangleList r (rect); for (int i = rects.size(); --i >= 0;) { const RectangleType& ourRect = rects.getReference (i); if (rect.intersects (ourRect)) { r.subtract (ourRect); if (r.rects.size() == 0) return; } } rects.addArray (r.rects); } else { rects.add (rect); } } } } /** Merges a new rectangle into the list. The rectangle being added will first be clipped to remove any parts of it that overlap existing rectangles in the list. */ void add (ValueType x, ValueType y, ValueType width, ValueType height) { add (RectangleType (x, y, width, height)); } /** Dumbly adds a rectangle to the list without checking for overlaps. This simply adds the rectangle to the end, it doesn't merge it or remove any overlapping bits. The rectangle can have any size and may be empty, but if it's floating point then it's expected to not contain any INF values. */ void addWithoutMerging (const RectangleType& rect) { jassert (rect.isFinite()); // You must provide a valid rectangle to this method! if (! rect.isEmpty()) rects.add (rect); } /** Merges another rectangle list into this one. Any overlaps between the two lists will be clipped, so that the result is the union of both lists. */ void add (const RectangleList& other) { for (const RectangleType* r = other.begin(), * const e = other.end(); r != e; ++r) add (*r); } /** Removes a rectangular region from the list. Any rectangles in the list which overlap this will be clipped and subdivided if necessary. */ void subtract (const RectangleType& rect) { const int originalNumRects = rects.size(); if (originalNumRects > 0) { const ValueType x1 = rect.getX(); const ValueType y1 = rect.getY(); const ValueType x2 = x1 + rect.getWidth(); const ValueType y2 = y1 + rect.getHeight(); for (int i = getNumRectangles(); --i >= 0;) { RectangleType& r = rects.getReference (i); const ValueType rx1 = r.getX(); const ValueType ry1 = r.getY(); const ValueType rx2 = rx1 + r.getWidth(); const ValueType ry2 = ry1 + r.getHeight(); if (! (x2 <= rx1 || x1 >= rx2 || y2 <= ry1 || y1 >= ry2)) { if (x1 > rx1 && x1 < rx2) { if (y1 <= ry1 && y2 >= ry2 && x2 >= rx2) { r.setWidth (x1 - rx1); } else { r.setX (x1); r.setWidth (rx2 - x1); rects.insert (++i, RectangleType (rx1, ry1, x1 - rx1, ry2 - ry1)); ++i; } } else if (x2 > rx1 && x2 < rx2) { r.setX (x2); r.setWidth (rx2 - x2); if (y1 > ry1 || y2 < ry2 || x1 > rx1) { rects.insert (++i, RectangleType (rx1, ry1, x2 - rx1, ry2 - ry1)); ++i; } } else if (y1 > ry1 && y1 < ry2) { if (x1 <= rx1 && x2 >= rx2 && y2 >= ry2) { r.setHeight (y1 - ry1); } else { r.setY (y1); r.setHeight (ry2 - y1); rects.insert (++i, RectangleType (rx1, ry1, rx2 - rx1, y1 - ry1)); ++i; } } else if (y2 > ry1 && y2 < ry2) { r.setY (y2); r.setHeight (ry2 - y2); if (x1 > rx1 || x2 < rx2 || y1 > ry1) { rects.insert (++i, RectangleType (rx1, ry1, rx2 - rx1, y2 - ry1)); ++i; } } else { rects.remove (i); } } } } } /** Removes all areas in another RectangleList from this one. Any rectangles in the list which overlap this will be clipped and subdivided if necessary. @returns true if the resulting list is non-empty. */ bool subtract (const RectangleList& otherList) { for (int i = otherList.rects.size(); --i >= 0 && rects.size() > 0;) subtract (otherList.rects.getReference (i)); return rects.size() > 0; } /** Removes any areas of the region that lie outside a given rectangle. Any rectangles in the list which overlap this will be clipped and subdivided if necessary. Returns true if the resulting region is not empty, false if it is empty. @see getIntersectionWith */ bool clipTo (const RectangleType& rect) { jassert (rect.isFinite()); // You must provide a valid rectangle to this method! bool notEmpty = false; if (rect.isEmpty()) { clear(); } else { for (int i = rects.size(); --i >= 0;) { RectangleType& r = rects.getReference (i); if (! rect.intersectRectangle (r)) rects.remove (i); else notEmpty = true; } } return notEmpty; } /** Removes any areas of the region that lie outside a given rectangle list. Any rectangles in this object which overlap the specified list will be clipped and subdivided if necessary. Returns true if the resulting region is not empty, false if it is empty. @see getIntersectionWith */ template bool clipTo (const RectangleList& other) { if (rects.size() == 0) return false; RectangleList result; for (int j = 0; j < rects.size(); ++j) { const RectangleType& rect = rects.getReference (j); for (const Rectangle* r = other.begin(), * const e = other.end(); r != e; ++r) { RectangleType clipped (r->template toType()); if (rect.intersectRectangle (clipped)) result.rects.add (clipped); } } swapWith (result); return ! isEmpty(); } /** Creates a region which is the result of clipping this one to a given rectangle. Unlike the other clipTo method, this one doesn't affect this object - it puts the resulting region into the list whose reference is passed-in. Returns true if the resulting region is not empty, false if it is empty. @see clipTo */ bool getIntersectionWith (const RectangleType& rect, RectangleList& destRegion) const { jassert (rect.isFinite()); // You must provide a valid rectangle to this method! destRegion.clear(); if (! rect.isEmpty()) { for (int i = rects.size(); --i >= 0;) { RectangleType r (rects.getReference (i)); if (rect.intersectRectangle (r)) destRegion.rects.add (r); } } return destRegion.rects.size() > 0; } /** Swaps the contents of this and another list. This swaps their internal pointers, so is hugely faster than using copy-by-value to swap them. */ void swapWith (RectangleList& otherList) noexcept { rects.swapWith (otherList.rects); } //============================================================================== /** Checks whether the region contains a given point. @returns true if the point lies within one of the rectangles in the list */ bool containsPoint (Point point) const noexcept { for (const RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) if (r->contains (point)) return true; return false; } /** Checks whether the region contains a given point. @returns true if the point lies within one of the rectangles in the list */ bool containsPoint (ValueType x, ValueType y) const noexcept { return containsPoint (Point (x, y)); } /** Checks whether the region contains the whole of a given rectangle. @returns true all parts of the rectangle passed in lie within the region defined by this object @see intersectsRectangle, containsPoint */ bool containsRectangle (const RectangleType& rectangleToCheck) const { if (rects.size() > 1) { RectangleList r (rectangleToCheck); for (int i = rects.size(); --i >= 0;) { r.subtract (rects.getReference (i)); if (r.rects.size() == 0) return true; } } else if (rects.size() > 0) { return rects.getReference (0).contains (rectangleToCheck); } return false; } /** Checks whether the region contains any part of a given rectangle. @returns true if any part of the rectangle passed in lies within the region defined by this object @see containsRectangle */ bool intersectsRectangle (const RectangleType& rectangleToCheck) const noexcept { for (const RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) if (r->intersects (rectangleToCheck)) return true; return false; } /** Checks whether this region intersects any part of another one. @see intersectsRectangle */ bool intersects (const RectangleList& other) const noexcept { for (const RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) if (other.intersectsRectangle (*r)) return true; return false; } //============================================================================== /** Returns the smallest rectangle that can enclose the whole of this region. */ RectangleType getBounds() const noexcept { if (rects.size() <= 1) { if (rects.size() == 0) return RectangleType(); return rects.getReference (0); } const RectangleType& r = rects.getReference (0); ValueType minX = r.getX(); ValueType minY = r.getY(); ValueType maxX = minX + r.getWidth(); ValueType maxY = minY + r.getHeight(); for (int i = rects.size(); --i > 0;) { const RectangleType& r2 = rects.getReference (i); minX = jmin (minX, r2.getX()); minY = jmin (minY, r2.getY()); maxX = jmax (maxX, r2.getRight()); maxY = jmax (maxY, r2.getBottom()); } return RectangleType (minX, minY, maxX - minX, maxY - minY); } /** Optimises the list into a minimum number of constituent rectangles. This will try to combine any adjacent rectangles into larger ones where possible, to simplify lists that might have been fragmented by repeated add/subtract calls. */ void consolidate() { for (int i = 0; i < getNumRectangles() - 1; ++i) { RectangleType& r = rects.getReference (i); const ValueType rx1 = r.getX(); const ValueType ry1 = r.getY(); const ValueType rx2 = rx1 + r.getWidth(); const ValueType ry2 = ry1 + r.getHeight(); for (int j = rects.size(); --j > i;) { RectangleType& r2 = rects.getReference (j); const ValueType jrx1 = r2.getX(); const ValueType jry1 = r2.getY(); const ValueType jrx2 = jrx1 + r2.getWidth(); const ValueType jry2 = jry1 + r2.getHeight(); // if the vertical edges of any blocks are touching and their horizontals don't // line up, split them horizontally.. if (jrx1 == rx2 || jrx2 == rx1) { if (jry1 > ry1 && jry1 < ry2) { r.setHeight (jry1 - ry1); rects.add (RectangleType (rx1, jry1, rx2 - rx1, ry2 - jry1)); i = -1; break; } if (jry2 > ry1 && jry2 < ry2) { r.setHeight (jry2 - ry1); rects.add (RectangleType (rx1, jry2, rx2 - rx1, ry2 - jry2)); i = -1; break; } else if (ry1 > jry1 && ry1 < jry2) { r2.setHeight (ry1 - jry1); rects.add (RectangleType (jrx1, ry1, jrx2 - jrx1, jry2 - ry1)); i = -1; break; } else if (ry2 > jry1 && ry2 < jry2) { r2.setHeight (ry2 - jry1); rects.add (RectangleType (jrx1, ry2, jrx2 - jrx1, jry2 - ry2)); i = -1; break; } } } } for (int i = 0; i < rects.size() - 1; ++i) { RectangleType& r = rects.getReference (i); for (int j = rects.size(); --j > i;) { if (r.enlargeIfAdjacent (rects.getReference (j))) { rects.remove (j); i = -1; break; } } } } /** Adds an x and y value to all the coordinates. */ void offsetAll (Point offset) noexcept { for (RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) *r += offset; } /** Adds an x and y value to all the coordinates. */ void offsetAll (ValueType dx, ValueType dy) noexcept { offsetAll (Point (dx, dy)); } /** Scales all the coordinates. */ template void scaleAll (ScaleType scaleFactor) noexcept { for (RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) *r *= scaleFactor; } /** Applies a transform to all the rectangles. Obviously this will create a mess if the transform involves any rotation or skewing. */ void transformAll (const AffineTransform& transform) noexcept { for (RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) *r = r->transformedBy (transform); } //============================================================================== /** Creates a Path object to represent this region. */ Path toPath() const { Path p; for (int i = 0; i < rects.size(); ++i) p.addRectangle (rects.getReference (i)); return p; } //============================================================================== /** Standard method for iterating the rectangles in the list. */ const RectangleType* begin() const noexcept { return rects.begin(); } /** Standard method for iterating the rectangles in the list. */ const RectangleType* end() const noexcept { return rects.end(); } /** Increases the internal storage to hold a minimum number of rectangles. Calling this before adding a large number of rectangles means that the array won't have to keep dynamically resizing itself as the elements are added, and it'll therefore be more efficient. @see Array::ensureStorageAllocated */ void ensureStorageAllocated (int minNumRectangles) { rects.ensureStorageAllocated (minNumRectangles); } private: //============================================================================== Array rects; }; #endif // JUCE_RECTANGLELIST_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/image_formats/000077500000000000000000000000001320201440200272275ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp000066400000000000000000000270121320201440200324770ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER Image juce_loadWithCoreImage (InputStream& input); #else //============================================================================== class GIFLoader { public: GIFLoader (InputStream& in) : input (in), dataBlockIsZero (false), fresh (false), finished (false), currentBit (0), lastBit (0), lastByteIndex (0), codeSize (0), setCodeSize (0), maxCode (0), maxCodeSize (0), firstcode (0), oldcode (0), clearCode (0), endCode (0) { int imageWidth, imageHeight; if (! getSizeFromHeader (imageWidth, imageHeight)) return; uint8 buf [16]; if (in.read (buf, 3) != 3) return; int numColours = 2 << (buf[0] & 7); int transparent = -1; if ((buf[0] & 0x80) != 0) readPalette (numColours); for (;;) { if (input.read (buf, 1) != 1 || buf[0] == ';') break; if (buf[0] == '!') { if (readExtension (transparent)) continue; break; } if (buf[0] != ',') continue; if (input.read (buf, 9) == 9) { imageWidth = (int) ByteOrder::littleEndianShort (buf + 4); imageHeight = (int) ByteOrder::littleEndianShort (buf + 6); numColours = 2 << (buf[8] & 7); if ((buf[8] & 0x80) != 0) if (! readPalette (numColours)) break; image = Image (transparent >= 0 ? Image::ARGB : Image::RGB, imageWidth, imageHeight, transparent >= 0); image.getProperties()->set ("originalImageHadAlpha", transparent >= 0); readImage ((buf[8] & 0x40) != 0, transparent); } break; } } Image image; private: InputStream& input; uint8 buffer [260]; PixelARGB palette [256]; bool dataBlockIsZero, fresh, finished; int currentBit, lastBit, lastByteIndex; int codeSize, setCodeSize; int maxCode, maxCodeSize; int firstcode, oldcode; int clearCode, endCode; enum { maxGifCode = 1 << 12 }; int table [2] [maxGifCode]; int stack [2 * maxGifCode]; int* sp; bool getSizeFromHeader (int& w, int& h) { char b[6]; if (input.read (b, 6) == 6 && (strncmp ("GIF87a", b, 6) == 0 || strncmp ("GIF89a", b, 6) == 0)) { if (input.read (b, 4) == 4) { w = (int) ByteOrder::littleEndianShort (b); h = (int) ByteOrder::littleEndianShort (b + 2); return w > 0 && h > 0; } } return false; } bool readPalette (const int numCols) { for (int i = 0; i < numCols; ++i) { uint8 rgb[4]; input.read (rgb, 3); palette[i].setARGB (0xff, rgb[0], rgb[1], rgb[2]); palette[i].premultiply(); } return true; } int readDataBlock (uint8* const dest) { uint8 n; if (input.read (&n, 1) == 1) { dataBlockIsZero = (n == 0); if (dataBlockIsZero || (input.read (dest, n) == n)) return n; } return -1; } int readExtension (int& transparent) { uint8 type; if (input.read (&type, 1) != 1) return false; uint8 b [260]; int n = 0; if (type == 0xf9) { n = readDataBlock (b); if (n < 0) return 1; if ((b[0] & 1) != 0) transparent = b[3]; } do { n = readDataBlock (b); } while (n > 0); return n >= 0; } void clearTable() { int i; for (i = 0; i < clearCode; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < maxGifCode; ++i) { table[0][i] = 0; table[1][i] = 0; } } void initialise (const int inputCodeSize) { setCodeSize = inputCodeSize; codeSize = setCodeSize + 1; clearCode = 1 << setCodeSize; endCode = clearCode + 1; maxCodeSize = 2 * clearCode; maxCode = clearCode + 2; getCode (0, true); fresh = true; clearTable(); sp = stack; } int readLZWByte() { if (fresh) { fresh = false; for (;;) { firstcode = oldcode = getCode (codeSize, false); if (firstcode != clearCode) return firstcode; } } if (sp > stack) return *--sp; int code; while ((code = getCode (codeSize, false)) >= 0) { if (code == clearCode) { clearTable(); codeSize = setCodeSize + 1; maxCodeSize = 2 * clearCode; maxCode = clearCode + 2; sp = stack; firstcode = oldcode = getCode (codeSize, false); return firstcode; } else if (code == endCode) { if (dataBlockIsZero) return -2; uint8 buf [260]; int n; while ((n = readDataBlock (buf)) > 0) {} if (n != 0) return -2; } const int incode = code; if (code >= maxCode) { *sp++ = firstcode; code = oldcode; } while (code >= clearCode) { *sp++ = table[1][code]; if (code == table[0][code]) return -2; code = table[0][code]; } *sp++ = firstcode = table[1][code]; if ((code = maxCode) < maxGifCode) { table[0][code] = oldcode; table[1][code] = firstcode; ++maxCode; if (maxCode >= maxCodeSize && maxCodeSize < maxGifCode) { maxCodeSize <<= 1; ++codeSize; } } oldcode = incode; if (sp > stack) return *--sp; } return code; } int getCode (const int codeSize_, const bool shouldInitialise) { if (shouldInitialise) { currentBit = 0; lastBit = 0; finished = false; return 0; } if ((currentBit + codeSize_) >= lastBit) { if (finished) return -1; buffer[0] = buffer [lastByteIndex - 2]; buffer[1] = buffer [lastByteIndex - 1]; const int n = readDataBlock (buffer + 2); if (n == 0) finished = true; lastByteIndex = 2 + n; currentBit = (currentBit - lastBit) + 16; lastBit = (2 + n) * 8 ; } int result = 0; int i = currentBit; for (int j = 0; j < codeSize_; ++j) { result |= ((buffer[i >> 3] & (1 << (i & 7))) != 0) << j; ++i; } currentBit += codeSize_; return result; } bool readImage (const int interlace, const int transparent) { uint8 c; if (input.read (&c, 1) != 1) return false; initialise (c); if (transparent >= 0) palette [transparent].setARGB (0, 0, 0, 0); int xpos = 0, ypos = 0, yStep = 8, pass = 0; const Image::BitmapData destData (image, Image::BitmapData::writeOnly); uint8* p = destData.getPixelPointer (0, 0); const bool hasAlpha = image.hasAlphaChannel(); for (;;) { const int index = readLZWByte(); if (index < 0) break; if (hasAlpha) ((PixelARGB*) p)->set (palette [index]); else ((PixelRGB*) p)->set (palette [index]); p += destData.pixelStride; if (++xpos == destData.width) { xpos = 0; if (interlace) { ypos += yStep; while (ypos >= destData.height) { switch (++pass) { case 1: ypos = 4; yStep = 8; break; case 2: ypos = 2; yStep = 4; break; case 3: ypos = 1; yStep = 2; break; default: return true; } } } else { if (++ypos >= destData.height) break; } p = destData.getPixelPointer (xpos, ypos); } } return true; } JUCE_DECLARE_NON_COPYABLE (GIFLoader) }; #endif //============================================================================== GIFImageFormat::GIFImageFormat() {} GIFImageFormat::~GIFImageFormat() {} String GIFImageFormat::getFormatName() { return "GIF"; } bool GIFImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("gif"); } bool GIFImageFormat::canUnderstand (InputStream& in) { char header [4]; return (in.read (header, sizeof (header)) == sizeof (header)) && header[0] == 'G' && header[1] == 'I' && header[2] == 'F'; } Image GIFImageFormat::decodeImage (InputStream& in) { #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER return juce_loadWithCoreImage (in); #else const ScopedPointer loader (new GIFLoader (in)); return loader->image; #endif } bool GIFImageFormat::writeImageToStream (const Image& /*sourceImage*/, OutputStream& /*destStream*/) { jassertfalse; // writing isn't implemented for GIFs! return false; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp000066400000000000000000000326641320201440200326300ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4365) #endif namespace jpeglibNamespace { #if JUCE_INCLUDE_JPEGLIB_CODE || ! defined (JUCE_INCLUDE_JPEGLIB_CODE) #if JUCE_MINGW typedef unsigned char boolean; #endif #if JUCE_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #define JPEG_INTERNALS #undef FAR #include "jpglib/jpeglib.h" #include "jpglib/jcapimin.c" #include "jpglib/jcapistd.c" #include "jpglib/jccoefct.c" #include "jpglib/jccolor.c" #undef FIX #include "jpglib/jcdctmgr.c" #undef CONST_BITS #include "jpglib/jchuff.c" #undef emit_byte #include "jpglib/jcinit.c" #include "jpglib/jcmainct.c" #include "jpglib/jcmarker.c" #include "jpglib/jcmaster.c" #include "jpglib/jcomapi.c" #include "jpglib/jcparam.c" #include "jpglib/jcphuff.c" #include "jpglib/jcprepct.c" #include "jpglib/jcsample.c" #include "jpglib/jctrans.c" #include "jpglib/jdapistd.c" #include "jpglib/jdapimin.c" #include "jpglib/jdatasrc.c" #include "jpglib/jdcoefct.c" #undef FIX #include "jpglib/jdcolor.c" #undef FIX #include "jpglib/jddctmgr.c" #undef CONST_BITS #undef ASSIGN_STATE #include "jpglib/jdhuff.c" #include "jpglib/jdinput.c" #include "jpglib/jdmainct.c" #include "jpglib/jdmarker.c" #include "jpglib/jdmaster.c" #undef FIX #include "jpglib/jdmerge.c" #undef ASSIGN_STATE #include "jpglib/jdphuff.c" #include "jpglib/jdpostct.c" #undef FIX #include "jpglib/jdsample.c" #include "jpglib/jdtrans.c" #include "jpglib/jfdctflt.c" #include "jpglib/jfdctint.c" #undef CONST_BITS #undef MULTIPLY #undef FIX_0_541196100 #include "jpglib/jfdctfst.c" #undef FIX_0_541196100 #include "jpglib/jidctflt.c" #undef CONST_BITS #undef FIX_1_847759065 #undef MULTIPLY #undef DEQUANTIZE #undef DESCALE #include "jpglib/jidctfst.c" #undef CONST_BITS #undef FIX_1_847759065 #undef MULTIPLY #undef DEQUANTIZE #include "jpglib/jidctint.c" #include "jpglib/jidctred.c" #include "jpglib/jmemmgr.c" #include "jpglib/jmemnobs.c" #include "jpglib/jquant1.c" #include "jpglib/jquant2.c" #include "jpglib/jutils.c" #include "jpglib/transupp.c" #if JUCE_CLANG #pragma clang diagnostic pop #endif #else #define JPEG_INTERNALS #undef FAR #include #endif } #undef max #undef min #if JUCE_MSVC #pragma warning (pop) #endif //============================================================================== namespace JPEGHelpers { using namespace jpeglibNamespace; #if ! JUCE_MSVC using jpeglibNamespace::boolean; #endif struct JPEGDecodingFailure {}; static void fatalErrorHandler (j_common_ptr) { throw JPEGDecodingFailure(); } static void silentErrorCallback1 (j_common_ptr) {} static void silentErrorCallback2 (j_common_ptr, int) {} static void silentErrorCallback3 (j_common_ptr, char*) {} static void setupSilentErrorHandler (struct jpeg_error_mgr& err) { zerostruct (err); err.error_exit = fatalErrorHandler; err.emit_message = silentErrorCallback2; err.output_message = silentErrorCallback1; err.format_message = silentErrorCallback3; err.reset_error_mgr = silentErrorCallback1; } //============================================================================== #if ! JUCE_USING_COREIMAGE_LOADER static void dummyCallback1 (j_decompress_ptr) {} static void jpegSkip (j_decompress_ptr decompStruct, long num) { decompStruct->src->next_input_byte += num; num = jmin (num, (long) decompStruct->src->bytes_in_buffer); decompStruct->src->bytes_in_buffer -= (size_t) num; } static boolean jpegFill (j_decompress_ptr) { return 0; } #endif //============================================================================== const int jpegBufferSize = 512; struct JuceJpegDest : public jpeg_destination_mgr { OutputStream* output; char* buffer; }; static void jpegWriteInit (j_compress_ptr) {} static void jpegWriteTerminate (j_compress_ptr cinfo) { JuceJpegDest* const dest = static_cast (cinfo->dest); const size_t numToWrite = jpegBufferSize - dest->free_in_buffer; dest->output->write (dest->buffer, numToWrite); } static boolean jpegWriteFlush (j_compress_ptr cinfo) { JuceJpegDest* const dest = static_cast (cinfo->dest); const int numToWrite = jpegBufferSize; dest->next_output_byte = reinterpret_cast (dest->buffer); dest->free_in_buffer = jpegBufferSize; return (boolean) dest->output->write (dest->buffer, (size_t) numToWrite); } } //============================================================================== JPEGImageFormat::JPEGImageFormat() : quality (-1.0f) { } JPEGImageFormat::~JPEGImageFormat() {} void JPEGImageFormat::setQuality (const float newQuality) { quality = newQuality; } String JPEGImageFormat::getFormatName() { return "JPEG"; } bool JPEGImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("jpeg;jpg"); } bool JPEGImageFormat::canUnderstand (InputStream& in) { const int bytesNeeded = 10; uint8 header [bytesNeeded]; return in.read (header, bytesNeeded) == bytesNeeded && header[0] == 0xff && header[1] == 0xd8 && header[2] == 0xff; } #if JUCE_USING_COREIMAGE_LOADER Image juce_loadWithCoreImage (InputStream& input); #endif Image JPEGImageFormat::decodeImage (InputStream& in) { #if JUCE_USING_COREIMAGE_LOADER return juce_loadWithCoreImage (in); #else using namespace jpeglibNamespace; using namespace JPEGHelpers; MemoryOutputStream mb; mb << in; Image image; if (mb.getDataSize() > 16) { struct jpeg_decompress_struct jpegDecompStruct; struct jpeg_error_mgr jerr; setupSilentErrorHandler (jerr); jpegDecompStruct.err = &jerr; jpeg_create_decompress (&jpegDecompStruct); jpegDecompStruct.src = (jpeg_source_mgr*)(jpegDecompStruct.mem->alloc_small) ((j_common_ptr)(&jpegDecompStruct), JPOOL_PERMANENT, sizeof (jpeg_source_mgr)); jpegDecompStruct.src->init_source = dummyCallback1; jpegDecompStruct.src->fill_input_buffer = jpegFill; jpegDecompStruct.src->skip_input_data = jpegSkip; jpegDecompStruct.src->resync_to_restart = jpeg_resync_to_restart; jpegDecompStruct.src->term_source = dummyCallback1; jpegDecompStruct.src->next_input_byte = static_cast (mb.getData()); jpegDecompStruct.src->bytes_in_buffer = mb.getDataSize(); try { jpeg_read_header (&jpegDecompStruct, TRUE); jpeg_calc_output_dimensions (&jpegDecompStruct); const int width = (int) jpegDecompStruct.output_width; const int height = (int) jpegDecompStruct.output_height; jpegDecompStruct.out_color_space = JCS_RGB; JSAMPARRAY buffer = (*jpegDecompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegDecompStruct, JPOOL_IMAGE, (JDIMENSION) width * 3, 1); if (jpeg_start_decompress (&jpegDecompStruct)) { image = Image (Image::RGB, width, height, false); image.getProperties()->set ("originalImageHadAlpha", false); const bool hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) const Image::BitmapData destData (image, Image::BitmapData::writeOnly); for (int y = 0; y < height; ++y) { jpeg_read_scanlines (&jpegDecompStruct, buffer, 1); const uint8* src = *buffer; uint8* dest = destData.getLinePointer (y); if (hasAlphaChan) { for (int i = width; --i >= 0;) { ((PixelARGB*) dest)->setARGB (0xff, src[0], src[1], src[2]); ((PixelARGB*) dest)->premultiply(); dest += destData.pixelStride; src += 3; } } else { for (int i = width; --i >= 0;) { ((PixelRGB*) dest)->setARGB (0xff, src[0], src[1], src[2]); dest += destData.pixelStride; src += 3; } } } jpeg_finish_decompress (&jpegDecompStruct); in.setPosition (((char*) jpegDecompStruct.src->next_input_byte) - (char*) mb.getData()); } jpeg_destroy_decompress (&jpegDecompStruct); } catch (...) {} } return image; #endif } bool JPEGImageFormat::writeImageToStream (const Image& image, OutputStream& out) { using namespace jpeglibNamespace; using namespace JPEGHelpers; jpeg_compress_struct jpegCompStruct; zerostruct (jpegCompStruct); jpeg_create_compress (&jpegCompStruct); struct jpeg_error_mgr jerr; setupSilentErrorHandler (jerr); jpegCompStruct.err = &jerr; JuceJpegDest dest; jpegCompStruct.dest = &dest; dest.output = &out; HeapBlock tempBuffer (jpegBufferSize); dest.buffer = tempBuffer; dest.next_output_byte = (JOCTET*) dest.buffer; dest.free_in_buffer = jpegBufferSize; dest.init_destination = jpegWriteInit; dest.empty_output_buffer = jpegWriteFlush; dest.term_destination = jpegWriteTerminate; jpegCompStruct.image_width = (JDIMENSION) image.getWidth(); jpegCompStruct.image_height = (JDIMENSION) image.getHeight(); jpegCompStruct.input_components = 3; jpegCompStruct.in_color_space = JCS_RGB; jpegCompStruct.write_JFIF_header = 1; jpegCompStruct.X_density = 72; jpegCompStruct.Y_density = 72; jpeg_set_defaults (&jpegCompStruct); jpegCompStruct.dct_method = JDCT_FLOAT; jpegCompStruct.optimize_coding = 1; if (quality < 0.0f) quality = 0.85f; jpeg_set_quality (&jpegCompStruct, jlimit (0, 100, roundToInt (quality * 100.0f)), TRUE); jpeg_start_compress (&jpegCompStruct, TRUE); const int strideBytes = (int) (jpegCompStruct.image_width * (unsigned int) jpegCompStruct.input_components); JSAMPARRAY buffer = (*jpegCompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegCompStruct, JPOOL_IMAGE, (JDIMENSION) strideBytes, 1); const Image::BitmapData srcData (image, Image::BitmapData::readOnly); while (jpegCompStruct.next_scanline < jpegCompStruct.image_height) { uint8* dst = *buffer; if (srcData.pixelFormat == Image::RGB) { const uint8* src = srcData.getLinePointer ((int) jpegCompStruct.next_scanline); for (int i = srcData.width; --i >= 0;) { *dst++ = ((const PixelRGB*) src)->getRed(); *dst++ = ((const PixelRGB*) src)->getGreen(); *dst++ = ((const PixelRGB*) src)->getBlue(); src += srcData.pixelStride; } } else { for (int x = 0; x < srcData.width; ++x) { const Colour pixel (srcData.getPixelColour (x, (int) jpegCompStruct.next_scanline)); *dst++ = pixel.getRed(); *dst++ = pixel.getGreen(); *dst++ = pixel.getBlue(); } } jpeg_write_scanlines (&jpegCompStruct, buffer, 1); } jpeg_finish_compress (&jpegCompStruct); jpeg_destroy_compress (&jpegCompStruct); return true; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp000066400000000000000000000431731320201440200325240ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4390 4611 4365 4267) #ifdef __INTEL_COMPILER #pragma warning (disable: 2544 2545) #endif #endif namespace zlibNamespace { #if JUCE_INCLUDE_ZLIB_CODE #undef OS_CODE #undef fdopen #include "../../juce_core/zip/zlib/zlib.h" #undef OS_CODE #else #include JUCE_ZLIB_INCLUDE_PATH #endif } namespace pnglibNamespace { using namespace zlibNamespace; #if JUCE_INCLUDE_PNGLIB_CODE || ! defined (JUCE_INCLUDE_PNGLIB_CODE) #if _MSC_VER != 1310 using std::calloc; // (causes conflict in VS.NET 2003) using std::malloc; using std::free; #endif #if JUCE_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #endif using std::abs; #define NO_DUMMY_DECL #define PNGLCONF_H 1 #if JUCE_ANDROID #define PNG_ARM_NEON_SUPPORTED #endif #define PNG_16BIT_SUPPORTED #define PNG_ALIGNED_MEMORY_SUPPORTED #define PNG_BENIGN_ERRORS_SUPPORTED #define PNG_BENIGN_READ_ERRORS_SUPPORTED #define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED #define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED #define PNG_COLORSPACE_SUPPORTED #define PNG_CONSOLE_IO_SUPPORTED #define PNG_EASY_ACCESS_SUPPORTED #define PNG_FIXED_POINT_SUPPORTED #define PNG_FLOATING_ARITHMETIC_SUPPORTED #define PNG_FLOATING_POINT_SUPPORTED #define PNG_FORMAT_AFIRST_SUPPORTED #define PNG_FORMAT_BGR_SUPPORTED #define PNG_GAMMA_SUPPORTED #define PNG_GET_PALETTE_MAX_SUPPORTED #define PNG_HANDLE_AS_UNKNOWN_SUPPORTED #define PNG_INCH_CONVERSIONS_SUPPORTED #define PNG_INFO_IMAGE_SUPPORTED #define PNG_IO_STATE_SUPPORTED #define PNG_MNG_FEATURES_SUPPORTED #define PNG_POINTER_INDEXING_SUPPORTED #define PNG_PROGRESSIVE_READ_SUPPORTED #define PNG_READ_16BIT_SUPPORTED #define PNG_READ_ALPHA_MODE_SUPPORTED #define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED #define PNG_READ_BACKGROUND_SUPPORTED #define PNG_READ_BGR_SUPPORTED #define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED #define PNG_READ_COMPOSITE_NODIV_SUPPORTED #define PNG_READ_COMPRESSED_TEXT_SUPPORTED #define PNG_READ_EXPAND_16_SUPPORTED #define PNG_READ_EXPAND_SUPPORTED #define PNG_READ_FILLER_SUPPORTED #define PNG_READ_GAMMA_SUPPORTED #define PNG_READ_GET_PALETTE_MAX_SUPPORTED #define PNG_READ_GRAY_TO_RGB_SUPPORTED #define PNG_READ_INTERLACING_SUPPORTED #define PNG_READ_INT_FUNCTIONS_SUPPORTED #define PNG_READ_INVERT_ALPHA_SUPPORTED #define PNG_READ_INVERT_SUPPORTED #define PNG_READ_OPT_PLTE_SUPPORTED #define PNG_READ_PACKSWAP_SUPPORTED #define PNG_READ_PACK_SUPPORTED #define PNG_READ_QUANTIZE_SUPPORTED #define PNG_READ_RGB_TO_GRAY_SUPPORTED #define PNG_READ_SCALE_16_TO_8_SUPPORTED #define PNG_READ_SHIFT_SUPPORTED #define PNG_READ_STRIP_16_TO_8_SUPPORTED #define PNG_READ_STRIP_ALPHA_SUPPORTED #define PNG_READ_SUPPORTED #define PNG_READ_SWAP_ALPHA_SUPPORTED #define PNG_READ_SWAP_SUPPORTED #define PNG_READ_TEXT_SUPPORTED #define PNG_READ_TRANSFORMS_SUPPORTED #define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED #define PNG_READ_USER_CHUNKS_SUPPORTED #define PNG_READ_USER_TRANSFORM_SUPPORTED #define PNG_READ_bKGD_SUPPORTED #define PNG_READ_cHRM_SUPPORTED #define PNG_READ_gAMA_SUPPORTED #define PNG_READ_hIST_SUPPORTED #define PNG_READ_iCCP_SUPPORTED #define PNG_READ_iTXt_SUPPORTED #define PNG_READ_oFFs_SUPPORTED #define PNG_READ_pCAL_SUPPORTED #define PNG_READ_pHYs_SUPPORTED #define PNG_READ_sBIT_SUPPORTED #define PNG_READ_sCAL_SUPPORTED #define PNG_READ_sPLT_SUPPORTED #define PNG_READ_sRGB_SUPPORTED #define PNG_READ_tEXt_SUPPORTED #define PNG_READ_tIME_SUPPORTED #define PNG_READ_tRNS_SUPPORTED #define PNG_READ_zTXt_SUPPORTED #define PNG_SAVE_INT_32_SUPPORTED #define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED #define PNG_SEQUENTIAL_READ_SUPPORTED #define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED #define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED #define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED #define PNG_SET_USER_LIMITS_SUPPORTED #define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED #define PNG_SIMPLIFIED_READ_BGR_SUPPORTED #define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED #define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED #define PNG_STDIO_SUPPORTED #define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED #define PNG_TEXT_SUPPORTED #define PNG_TIME_RFC1123_SUPPORTED #define PNG_UNKNOWN_CHUNKS_SUPPORTED #define PNG_USER_CHUNKS_SUPPORTED #define PNG_USER_LIMITS_SUPPORTED #define PNG_USER_TRANSFORM_INFO_SUPPORTED #define PNG_USER_TRANSFORM_PTR_SUPPORTED #define PNG_WARNINGS_SUPPORTED #define PNG_WRITE_16BIT_SUPPORTED #define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED #define PNG_WRITE_BGR_SUPPORTED #define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED #define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED #define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED #define PNG_WRITE_FILLER_SUPPORTED #define PNG_WRITE_FILTER_SUPPORTED #define PNG_WRITE_FLUSH_SUPPORTED #define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED #define PNG_WRITE_INTERLACING_SUPPORTED #define PNG_WRITE_INT_FUNCTIONS_SUPPORTED #define PNG_WRITE_INVERT_ALPHA_SUPPORTED #define PNG_WRITE_INVERT_SUPPORTED #define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED #define PNG_WRITE_PACKSWAP_SUPPORTED #define PNG_WRITE_PACK_SUPPORTED #define PNG_WRITE_SHIFT_SUPPORTED #define PNG_WRITE_SUPPORTED #define PNG_WRITE_SWAP_ALPHA_SUPPORTED #define PNG_WRITE_SWAP_SUPPORTED #define PNG_WRITE_TEXT_SUPPORTED #define PNG_WRITE_TRANSFORMS_SUPPORTED #define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED #define PNG_WRITE_USER_TRANSFORM_SUPPORTED #define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #define PNG_WRITE_bKGD_SUPPORTED #define PNG_WRITE_cHRM_SUPPORTED #define PNG_WRITE_gAMA_SUPPORTED #define PNG_WRITE_hIST_SUPPORTED #define PNG_WRITE_iCCP_SUPPORTED #define PNG_WRITE_iTXt_SUPPORTED #define PNG_WRITE_oFFs_SUPPORTED #define PNG_WRITE_pCAL_SUPPORTED #define PNG_WRITE_pHYs_SUPPORTED #define PNG_WRITE_sBIT_SUPPORTED #define PNG_WRITE_sCAL_SUPPORTED #define PNG_WRITE_sPLT_SUPPORTED #define PNG_WRITE_sRGB_SUPPORTED #define PNG_WRITE_tEXt_SUPPORTED #define PNG_WRITE_tIME_SUPPORTED #define PNG_WRITE_tRNS_SUPPORTED #define PNG_WRITE_zTXt_SUPPORTED #define PNG_bKGD_SUPPORTED #define PNG_cHRM_SUPPORTED #define PNG_gAMA_SUPPORTED #define PNG_hIST_SUPPORTED #define PNG_iCCP_SUPPORTED #define PNG_iTXt_SUPPORTED #define PNG_oFFs_SUPPORTED #define PNG_pCAL_SUPPORTED #define PNG_pHYs_SUPPORTED #define PNG_sBIT_SUPPORTED #define PNG_sCAL_SUPPORTED #define PNG_sPLT_SUPPORTED #define PNG_sRGB_SUPPORTED #define PNG_tEXt_SUPPORTED #define PNG_tIME_SUPPORTED #define PNG_tRNS_SUPPORTED #define PNG_zTXt_SUPPORTED #define PNG_STRING_COPYRIGHT ""; #define PNG_STRING_NEWLINE "\n" #define PNG_LITERAL_SHARP 0x23 #define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b #define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d #define PNG_API_RULE 0 #define PNG_CALLOC_SUPPORTED #define PNG_COST_SHIFT 3 #define PNG_DEFAULT_READ_MACROS 1 #define PNG_GAMMA_THRESHOLD_FIXED 5000 #define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE #define PNG_INFLATE_BUF_SIZE 1024 #define PNG_MAX_GAMMA_8 11 #define PNG_QUANTIZE_BLUE_BITS 5 #define PNG_QUANTIZE_GREEN_BITS 5 #define PNG_QUANTIZE_RED_BITS 5 #define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) #define PNG_TEXT_Z_DEFAULT_STRATEGY 0 #define PNG_WEIGHT_SHIFT 8 #define PNG_ZBUF_SIZE 8192 #define PNG_Z_DEFAULT_COMPRESSION (-1) #define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 #define PNG_Z_DEFAULT_STRATEGY 1 #define PNG_sCAL_PRECISION 5 #define PNG_sRGB_PROFILE_CHECKS 2 #define png_debug(a, b) #define png_debug1(a, b, c) #define png_debug2(a, b, c, d) #include "pnglib/png.h" #include "pnglib/pngconf.h" #define PNG_NO_EXTERN #include "pnglib/png.c" #include "pnglib/pngerror.c" #include "pnglib/pngget.c" #include "pnglib/pngmem.c" #include "pnglib/pngread.c" #include "pnglib/pngpread.c" #include "pnglib/pngrio.c" #include "pnglib/pngrtran.c" #include "pnglib/pngrutil.c" #include "pnglib/pngset.c" #include "pnglib/pngtrans.c" #include "pnglib/pngwio.c" #include "pnglib/pngwrite.c" #include "pnglib/pngwtran.c" #include "pnglib/pngwutil.c" #if JUCE_CLANG #pragma clang diagnostic pop #endif #else extern "C" { #include #include } #endif } #undef max #undef min #undef fdopen #if JUCE_MSVC #pragma warning (pop) #endif //============================================================================== namespace PNGHelpers { using namespace pnglibNamespace; static void JUCE_CDECL writeDataCallback (png_structp png, png_bytep data, png_size_t length) { static_cast (png_get_io_ptr (png))->write (data, length); } #if ! JUCE_USING_COREIMAGE_LOADER static void JUCE_CDECL readCallback (png_structp png, png_bytep data, png_size_t length) { static_cast (png_get_io_ptr (png))->read (data, (int) length); } struct PNGErrorStruct {}; static void JUCE_CDECL errorCallback (png_structp, png_const_charp) { throw PNGErrorStruct(); } static void JUCE_CDECL warningCallback (png_structp, png_const_charp) {} #endif } //============================================================================== PNGImageFormat::PNGImageFormat() {} PNGImageFormat::~PNGImageFormat() {} String PNGImageFormat::getFormatName() { return "PNG"; } bool PNGImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("png"); } bool PNGImageFormat::canUnderstand (InputStream& in) { const int bytesNeeded = 4; char header [bytesNeeded]; return in.read (header, bytesNeeded) == bytesNeeded && header[1] == 'P' && header[2] == 'N' && header[3] == 'G'; } #if JUCE_USING_COREIMAGE_LOADER Image juce_loadWithCoreImage (InputStream& input); #endif Image PNGImageFormat::decodeImage (InputStream& in) { #if JUCE_USING_COREIMAGE_LOADER return juce_loadWithCoreImage (in); #else using namespace pnglibNamespace; Image image; if (png_structp pngReadStruct = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0)) { try { png_infop pngInfoStruct = png_create_info_struct (pngReadStruct); if (pngInfoStruct == nullptr) { png_destroy_read_struct (&pngReadStruct, 0, 0); return Image::null; } png_set_error_fn (pngReadStruct, 0, PNGHelpers::errorCallback, PNGHelpers::warningCallback); // read the header.. png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); png_uint_32 width = 0, height = 0; int bitDepth = 0, colorType = 0, interlaceType; png_read_info (pngReadStruct, pngInfoStruct); png_get_IHDR (pngReadStruct, pngInfoStruct, &width, &height, &bitDepth, &colorType, &interlaceType, 0, 0); if (bitDepth == 16) png_set_strip_16 (pngReadStruct); if (colorType == PNG_COLOR_TYPE_PALETTE) png_set_expand (pngReadStruct); if (bitDepth < 8) png_set_expand (pngReadStruct); if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) png_set_expand (pngReadStruct); if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb (pngReadStruct); png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 || pngInfoStruct->num_trans > 0; // Load the image into a temp buffer in the pnglib format.. const size_t lineStride = width * 4; HeapBlock tempBuffer (height * lineStride); HeapBlock rows (height); for (size_t y = 0; y < height; ++y) rows[y] = (png_bytep) (tempBuffer + lineStride * y); try { png_read_image (pngReadStruct, rows); png_read_end (pngReadStruct, pngInfoStruct); } catch (PNGHelpers::PNGErrorStruct&) {} png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); // now convert the data to a juce image format.. image = Image (hasAlphaChan ? Image::ARGB : Image::RGB, (int) width, (int) height, hasAlphaChan); image.getProperties()->set ("originalImageHadAlpha", image.hasAlphaChannel()); hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) const Image::BitmapData destData (image, Image::BitmapData::writeOnly); for (int y = 0; y < (int) height; ++y) { const uint8* src = rows[y]; uint8* dest = destData.getLinePointer (y); if (hasAlphaChan) { for (int i = (int) width; --i >= 0;) { ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); ((PixelARGB*) dest)->premultiply(); dest += destData.pixelStride; src += 4; } } else { for (int i = (int) width; --i >= 0;) { ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); dest += destData.pixelStride; src += 4; } } } } catch (PNGHelpers::PNGErrorStruct&) {} } return image; #endif } bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out) { using namespace pnglibNamespace; const int width = image.getWidth(); const int height = image.getHeight(); png_structp pngWriteStruct = png_create_write_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0); if (pngWriteStruct == nullptr) return false; png_infop pngInfoStruct = png_create_info_struct (pngWriteStruct); if (pngInfoStruct == nullptr) { png_destroy_write_struct (&pngWriteStruct, (png_infopp) nullptr); return false; } png_set_write_fn (pngWriteStruct, &out, PNGHelpers::writeDataCallback, 0); png_set_IHDR (pngWriteStruct, pngInfoStruct, (png_uint_32) width, (png_uint_32) height, 8, image.hasAlphaChannel() ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); HeapBlock rowData ((size_t) width * 4); png_color_8 sig_bit; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; sig_bit.gray = 0; sig_bit.alpha = 8; png_set_sBIT (pngWriteStruct, pngInfoStruct, &sig_bit); png_write_info (pngWriteStruct, pngInfoStruct); png_set_shift (pngWriteStruct, &sig_bit); png_set_packing (pngWriteStruct); const Image::BitmapData srcData (image, Image::BitmapData::readOnly); for (int y = 0; y < height; ++y) { uint8* dst = rowData; const uint8* src = srcData.getLinePointer (y); if (image.hasAlphaChannel()) { for (int i = width; --i >= 0;) { PixelARGB p (*(const PixelARGB*) src); p.unpremultiply(); *dst++ = p.getRed(); *dst++ = p.getGreen(); *dst++ = p.getBlue(); *dst++ = p.getAlpha(); src += srcData.pixelStride; } } else { for (int i = width; --i >= 0;) { *dst++ = ((const PixelRGB*) src)->getRed(); *dst++ = ((const PixelRGB*) src)->getGreen(); *dst++ = ((const PixelRGB*) src)->getBlue(); src += srcData.pixelStride; } } png_bytep rowPtr = rowData; png_write_rows (pngWriteStruct, &rowPtr, 1); } png_write_end (pngWriteStruct, pngInfoStruct); png_destroy_write_struct (&pngWriteStruct, &pngInfoStruct); return true; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/000077500000000000000000000000001320201440200256575ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp000066400000000000000000000516671320201440200304320ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, const int h) : pixelFormat (format), width (w), height (h) { jassert (format == Image::RGB || format == Image::ARGB || format == Image::SingleChannel); jassert (w > 0 && h > 0); // It's illegal to create a zero-sized image! } ImagePixelData::~ImagePixelData() { listeners.call (&Listener::imageDataBeingDeleted, this); } void ImagePixelData::sendDataChangeMessage() { listeners.call (&Listener::imageDataChanged, this); } int ImagePixelData::getSharedCount() const noexcept { return getReferenceCount(); } //============================================================================== ImageType::ImageType() {} ImageType::~ImageType() {} Image ImageType::convert (const Image& source) const { if (source.isNull() || getTypeID() == (ScopedPointer (source.getPixelData()->createType())->getTypeID())) return source; const Image::BitmapData src (source, Image::BitmapData::readOnly); Image newImage (create (src.pixelFormat, src.width, src.height, false)); Image::BitmapData dest (newImage, Image::BitmapData::writeOnly); jassert (src.pixelStride == dest.pixelStride && src.pixelFormat == dest.pixelFormat); for (int y = 0; y < dest.height; ++y) memcpy (dest.getLinePointer (y), src.getLinePointer (y), (size_t) dest.lineStride); return newImage; } //============================================================================== class SoftwarePixelData : public ImagePixelData { public: SoftwarePixelData (const Image::PixelFormat format_, const int w, const int h, const bool clearImage) : ImagePixelData (format_, w, h), pixelStride (format_ == Image::RGB ? 3 : ((format_ == Image::ARGB) ? 4 : 1)), lineStride ((pixelStride * jmax (1, w) + 3) & ~3) { imageData.allocate ((size_t) (lineStride * jmax (1, h)), clearImage); } LowLevelGraphicsContext* createLowLevelContext() override { sendDataChangeMessage(); return new LowLevelGraphicsSoftwareRenderer (Image (this)); } void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; if (mode != Image::BitmapData::readOnly) sendDataChangeMessage(); } ImagePixelData* clone() override { SoftwarePixelData* s = new SoftwarePixelData (pixelFormat, width, height, false); memcpy (s->imageData, imageData, (size_t) (lineStride * height)); return s; } ImageType* createType() const override { return new SoftwareImageType(); } private: HeapBlock imageData; const int pixelStride, lineStride; JUCE_LEAK_DETECTOR (SoftwarePixelData) }; SoftwareImageType::SoftwareImageType() {} SoftwareImageType::~SoftwareImageType() {} ImagePixelData::Ptr SoftwareImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { return new SoftwarePixelData (format, width, height, clearImage); } int SoftwareImageType::getTypeID() const { return 2; } //============================================================================== NativeImageType::NativeImageType() {} NativeImageType::~NativeImageType() {} int NativeImageType::getTypeID() const { return 1; } #if JUCE_WINDOWS || JUCE_LINUX ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { return new SoftwarePixelData (format, width, height, clearImage); } #endif //============================================================================== class SubsectionPixelData : public ImagePixelData { public: SubsectionPixelData (ImagePixelData* const im, const Rectangle& r) : ImagePixelData (im->pixelFormat, r.getWidth(), r.getHeight()), image (im), area (r) { } LowLevelGraphicsContext* createLowLevelContext() override { LowLevelGraphicsContext* g = image->createLowLevelContext(); g->clipToRectangle (area); g->setOrigin (area.getPosition()); return g; } void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode); if (mode != Image::BitmapData::readOnly) sendDataChangeMessage(); } ImagePixelData* clone() override { jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting) const ScopedPointer type (image->createType()); Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB)); { Graphics g (newImage); g.drawImageAt (Image (this), 0, 0); } newImage.getPixelData()->incReferenceCount(); return newImage.getPixelData(); } ImageType* createType() const override { return image->createType(); } /* as we always hold a reference to image, don't double count */ int getSharedCount() const noexcept override { return getReferenceCount() + image->getSharedCount() - 1; } private: friend class Image; const ImagePixelData::Ptr image; const Rectangle area; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData) }; Image Image::getClippedImage (const Rectangle& area) const { if (area.contains (getBounds())) return *this; const Rectangle validArea (area.getIntersection (getBounds())); return Image (validArea.isEmpty() ? nullptr : new SubsectionPixelData (image, validArea)); } //============================================================================== Image::Image() noexcept { } Image::Image (ImagePixelData* const instance) noexcept : image (instance) { } Image::Image (const PixelFormat format, int width, int height, bool clearImage) : image (NativeImageType().create (format, width, height, clearImage)) { } Image::Image (const PixelFormat format, int width, int height, bool clearImage, const ImageType& type) : image (type.create (format, width, height, clearImage)) { } Image::Image (const Image& other) noexcept : image (other.image) { } Image& Image::operator= (const Image& other) { image = other.image; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Image::Image (Image&& other) noexcept : image (static_cast (other.image)) { } Image& Image::operator= (Image&& other) noexcept { image = static_cast (other.image); return *this; } #endif Image::~Image() { } const Image Image::null; int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getSharedCount(); } int Image::getWidth() const noexcept { return image == nullptr ? 0 : image->width; } int Image::getHeight() const noexcept { return image == nullptr ? 0 : image->height; } Rectangle Image::getBounds() const noexcept { return image == nullptr ? Rectangle() : Rectangle (image->width, image->height); } Image::PixelFormat Image::getFormat() const noexcept { return image == nullptr ? UnknownFormat : image->pixelFormat; } bool Image::isARGB() const noexcept { return getFormat() == ARGB; } bool Image::isRGB() const noexcept { return getFormat() == RGB; } bool Image::isSingleChannel() const noexcept { return getFormat() == SingleChannel; } bool Image::hasAlphaChannel() const noexcept { return getFormat() != RGB; } LowLevelGraphicsContext* Image::createLowLevelContext() const { return image == nullptr ? nullptr : image->createLowLevelContext(); } void Image::duplicateIfShared() { if (getReferenceCount() > 1) image = image->clone(); } Image Image::createCopy() const { if (image != nullptr) return Image (image->clone()); return Image(); } Image Image::rescaled (const int newWidth, const int newHeight, const Graphics::ResamplingQuality quality) const { if (image == nullptr || (image->width == newWidth && image->height == newHeight)) return *this; const ScopedPointer type (image->createType()); Image newImage (type->create (image->pixelFormat, newWidth, newHeight, hasAlphaChannel())); Graphics g (newImage); g.setImageResamplingQuality (quality); g.drawImageTransformed (*this, AffineTransform::scale (newWidth / (float) image->width, newHeight / (float) image->height), false); return newImage; } Image Image::convertedToFormat (PixelFormat newFormat) const { if (image == nullptr || newFormat == image->pixelFormat) return *this; const int w = image->width, h = image->height; const ScopedPointer type (image->createType()); Image newImage (type->create (newFormat, w, h, false)); if (newFormat == SingleChannel) { if (! hasAlphaChannel()) { newImage.clear (getBounds(), Colours::black); } else { const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly); const BitmapData srcData (*this, 0, 0, w, h); for (int y = 0; y < h; ++y) { const PixelARGB* const src = (const PixelARGB*) srcData.getLinePointer (y); uint8* const dst = destData.getLinePointer (y); for (int x = 0; x < w; ++x) dst[x] = src[x].getAlpha(); } } } else if (image->pixelFormat == SingleChannel && newFormat == Image::ARGB) { const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly); const BitmapData srcData (*this, 0, 0, w, h); for (int y = 0; y < h; ++y) { const PixelAlpha* const src = (const PixelAlpha*) srcData.getLinePointer (y); PixelARGB* const dst = (PixelARGB*) destData.getLinePointer (y); for (int x = 0; x < w; ++x) dst[x].set (src[x]); } } else { if (hasAlphaChannel()) newImage.clear (getBounds()); Graphics g (newImage); g.drawImageAt (*this, 0, 0); } return newImage; } NamedValueSet* Image::getProperties() const { return image == nullptr ? nullptr : &(image->userData); } //============================================================================== Image::BitmapData::BitmapData (Image& im, const int x, const int y, const int w, const int h, BitmapData::ReadWriteMode mode) : width (w), height (h) { // The BitmapData class must be given a valid image, and a valid rectangle within it! jassert (im.image != nullptr); jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= im.getWidth() && y + h <= im.getHeight()); im.image->initialiseBitmapData (*this, x, y, mode); jassert (data != nullptr && pixelStride > 0 && lineStride != 0); } Image::BitmapData::BitmapData (const Image& im, const int x, const int y, const int w, const int h) : width (w), height (h) { // The BitmapData class must be given a valid image, and a valid rectangle within it! jassert (im.image != nullptr); jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= im.getWidth() && y + h <= im.getHeight()); im.image->initialiseBitmapData (*this, x, y, readOnly); jassert (data != nullptr && pixelStride > 0 && lineStride != 0); } Image::BitmapData::BitmapData (const Image& im, BitmapData::ReadWriteMode mode) : width (im.getWidth()), height (im.getHeight()) { // The BitmapData class must be given a valid image! jassert (im.image != nullptr); im.image->initialiseBitmapData (*this, 0, 0, mode); jassert (data != nullptr && pixelStride > 0 && lineStride != 0); } Image::BitmapData::~BitmapData() { } Colour Image::BitmapData::getPixelColour (const int x, const int y) const noexcept { jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height)); const uint8* const pixel = getPixelPointer (x, y); switch (pixelFormat) { case Image::ARGB: return Colour ( ((const PixelARGB*) pixel)->getUnpremultiplied()); case Image::RGB: return Colour (*((const PixelRGB*) pixel)); case Image::SingleChannel: return Colour (*((const PixelAlpha*) pixel)); default: jassertfalse; break; } return Colour(); } void Image::BitmapData::setPixelColour (const int x, const int y, Colour colour) const noexcept { jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height)); uint8* const pixel = getPixelPointer (x, y); const PixelARGB col (colour.getPixelARGB()); switch (pixelFormat) { case Image::ARGB: ((PixelARGB*) pixel)->set (col); break; case Image::RGB: ((PixelRGB*) pixel)->set (col); break; case Image::SingleChannel: ((PixelAlpha*) pixel)->set (col); break; default: jassertfalse; break; } } //============================================================================== void Image::clear (const Rectangle& area, Colour colourToClearTo) { const ScopedPointer g (image->createLowLevelContext()); g->setFill (colourToClearTo); g->fillRect (area, true); } //============================================================================== Colour Image::getPixelAt (const int x, const int y) const { if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())) { const BitmapData srcData (*this, x, y, 1, 1); return srcData.getPixelColour (0, 0); } return Colour(); } void Image::setPixelAt (const int x, const int y, Colour colour) { if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())) { const BitmapData destData (*this, x, y, 1, 1, BitmapData::writeOnly); destData.setPixelColour (0, 0, colour); } } void Image::multiplyAlphaAt (const int x, const int y, const float multiplier) { if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()) && hasAlphaChannel()) { const BitmapData destData (*this, x, y, 1, 1, BitmapData::readWrite); if (isARGB()) ((PixelARGB*) destData.data)->multiplyAlpha (multiplier); else *(destData.data) = (uint8) (*(destData.data) * multiplier); } } template struct PixelIterator { template static void iterate (const Image::BitmapData& data, const PixelOperation& pixelOp) { for (int y = 0; y < data.height; ++y) { uint8* p = data.getLinePointer (y); for (int x = 0; x < data.width; ++x) { pixelOp (*(PixelType*) p); p += data.pixelStride; } } } }; template static void performPixelOp (const Image::BitmapData& data, const PixelOperation& pixelOp) { switch (data.pixelFormat) { case Image::ARGB: PixelIterator ::iterate (data, pixelOp); break; case Image::RGB: PixelIterator ::iterate (data, pixelOp); break; case Image::SingleChannel: PixelIterator::iterate (data, pixelOp); break; default: jassertfalse; break; } } struct AlphaMultiplyOp { AlphaMultiplyOp (float alpha_) noexcept : alpha (alpha_) {} const float alpha; template void operator() (PixelType& pixel) const { pixel.multiplyAlpha (alpha); } JUCE_DECLARE_NON_COPYABLE (AlphaMultiplyOp) }; void Image::multiplyAllAlphas (const float amountToMultiplyBy) { jassert (hasAlphaChannel()); const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite); performPixelOp (destData, AlphaMultiplyOp (amountToMultiplyBy)); } struct DesaturateOp { template void operator() (PixelType& pixel) const { pixel.desaturate(); } }; void Image::desaturate() { if (isARGB() || isRGB()) { const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite); performPixelOp (destData, DesaturateOp()); } } void Image::createSolidAreaMask (RectangleList& result, const float alphaThreshold) const { if (hasAlphaChannel()) { const uint8 threshold = (uint8) jlimit (0, 255, roundToInt (alphaThreshold * 255.0f)); SparseSet pixelsOnRow; const BitmapData srcData (*this, 0, 0, getWidth(), getHeight()); for (int y = 0; y < srcData.height; ++y) { pixelsOnRow.clear(); const uint8* lineData = srcData.getLinePointer (y); if (isARGB()) { for (int x = 0; x < srcData.width; ++x) { if (((const PixelARGB*) lineData)->getAlpha() >= threshold) pixelsOnRow.addRange (Range (x, x + 1)); lineData += srcData.pixelStride; } } else { for (int x = 0; x < srcData.width; ++x) { if (*lineData >= threshold) pixelsOnRow.addRange (Range (x, x + 1)); lineData += srcData.pixelStride; } } for (int i = 0; i < pixelsOnRow.getNumRanges(); ++i) { const Range range (pixelsOnRow.getRange (i)); result.add (Rectangle (range.getStart(), y, range.getLength(), 1)); } result.consolidate(); } } else { result.add (0, 0, getWidth(), getHeight()); } } void Image::moveImageSection (int dx, int dy, int sx, int sy, int w, int h) { if (dx < 0) { w += dx; sx -= dx; dx = 0; } if (dy < 0) { h += dy; sy -= dy; dy = 0; } if (sx < 0) { w += sx; dx -= sx; sx = 0; } if (sy < 0) { h += sy; dy -= sy; sy = 0; } const int minX = jmin (dx, sx); const int minY = jmin (dy, sy); w = jmin (w, getWidth() - jmax (sx, dx)); h = jmin (h, getHeight() - jmax (sy, dy)); if (w > 0 && h > 0) { const int maxX = jmax (dx, sx) + w; const int maxY = jmax (dy, sy) + h; const BitmapData destData (*this, minX, minY, maxX - minX, maxY - minY, BitmapData::readWrite); uint8* dst = destData.getPixelPointer (dx - minX, dy - minY); const uint8* src = destData.getPixelPointer (sx - minX, sy - minY); const size_t lineSize = (size_t) (destData.pixelStride * w); if (dy > sy) { while (--h >= 0) { const int offset = h * destData.lineStride; memmove (dst + offset, src + offset, lineSize); } } else if (dst != src) { while (--h >= 0) { memmove (dst, src, lineSize); dst += destData.lineStride; src += destData.lineStride; } } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h000066400000000000000000000547471320201440200301010ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IMAGE_H_INCLUDED #define JUCE_IMAGE_H_INCLUDED class ImageType; class ImagePixelData; //============================================================================== /** Holds a fixed-size bitmap. The image is stored in either 24-bit RGB or 32-bit premultiplied-ARGB format. To draw into an image, create a Graphics object for it. e.g. @code // create a transparent 500x500 image.. Image myImage (Image::RGB, 500, 500, true); Graphics g (myImage); g.setColour (Colours::red); g.fillEllipse (20, 20, 300, 200); // draws a red ellipse in our image. @endcode Other useful ways to create an image are with the ImageCache class, or the ImageFileFormat, which provides a way to load common image files. @see Graphics, ImageFileFormat, ImageCache, ImageConvolutionKernel */ class JUCE_API Image { public: //============================================================================== /** */ enum PixelFormat { UnknownFormat, RGB, /**<< each pixel is a 3-byte packed RGB colour value. For byte order, see the PixelRGB class. */ ARGB, /**<< each pixel is a 4-byte ARGB premultiplied colour value. For byte order, see the PixelARGB class. */ SingleChannel /**<< each pixel is a 1-byte alpha channel value. */ }; //============================================================================== /** Creates a null image. */ Image() noexcept; /** Creates an image with a specified size and format. The image's internal type will be of the NativeImageType class - to specify a different type, use the other constructor, which takes an ImageType to use. @param format the number of colour channels in the image @param imageWidth the desired width of the image, in pixels - this value must be greater than zero (otherwise a width of 1 will be used) @param imageHeight the desired width of the image, in pixels - this value must be greater than zero (otherwise a height of 1 will be used) @param clearImage if true, the image will initially be cleared to black (if it's RGB) or transparent black (if it's ARGB). If false, the image may contain junk initially, so you need to make sure you overwrite it thoroughly. */ Image (PixelFormat format, int imageWidth, int imageHeight, bool clearImage); /** Creates an image with a specified size and format. @param format the number of colour channels in the image @param imageWidth the desired width of the image, in pixels - this value must be greater than zero (otherwise a width of 1 will be used) @param imageHeight the desired width of the image, in pixels - this value must be greater than zero (otherwise a height of 1 will be used) @param clearImage if true, the image will initially be cleared to black (if it's RGB) or transparent black (if it's ARGB). If false, the image may contain junk initially, so you need to make sure you overwrite it thoroughly. @param type the type of image - this lets you specify the internal format that will be used to allocate and manage the image data. */ Image (PixelFormat format, int imageWidth, int imageHeight, bool clearImage, const ImageType& type); /** Creates a shared reference to another image. This won't create a duplicate of the image - when Image objects are copied, they simply point to the same shared image data. To make sure that an Image object has its own unique, unshared internal data, call duplicateIfShared(). */ Image (const Image&) noexcept; /** Makes this image refer to the same underlying image as another object. This won't create a duplicate of the image - when Image objects are copied, they simply point to the same shared image data. To make sure that an Image object has its own unique, unshared internal data, call duplicateIfShared(). */ Image& operator= (const Image&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Image (Image&&) noexcept; Image& operator= (Image&&) noexcept; #endif /** Destructor. */ ~Image(); /** Returns true if the two images are referring to the same internal, shared image. */ bool operator== (const Image& other) const noexcept { return image == other.image; } /** Returns true if the two images are not referring to the same internal, shared image. */ bool operator!= (const Image& other) const noexcept { return image != other.image; } /** Returns true if this image isn't null. If you create an Image with the default constructor, it has no size or content, and is null until you reassign it to an Image which contains some actual data. The isNull() method is the opposite of isValid(). @see isNull */ inline bool isValid() const noexcept { return image != nullptr; } /** Returns true if this image is not valid. If you create an Image with the default constructor, it has no size or content, and is null until you reassign it to an Image which contains some actual data. The isNull() method is the opposite of isValid(). @see isValid */ inline bool isNull() const noexcept { return image == nullptr; } /** A null Image object that can be used when you need to return an invalid image. This object is the equivalient to an Image created with the default constructor. */ static const Image null; //============================================================================== /** Returns the image's width (in pixels). */ int getWidth() const noexcept; /** Returns the image's height (in pixels). */ int getHeight() const noexcept; /** Returns a rectangle with the same size as this image. The rectangle's origin is always (0, 0). */ Rectangle getBounds() const noexcept; /** Returns the image's pixel format. */ PixelFormat getFormat() const noexcept; /** True if the image's format is ARGB. */ bool isARGB() const noexcept; /** True if the image's format is RGB. */ bool isRGB() const noexcept; /** True if the image's format is a single-channel alpha map. */ bool isSingleChannel() const noexcept; /** True if the image contains an alpha-channel. */ bool hasAlphaChannel() const noexcept; //============================================================================== /** Clears a section of the image with a given colour. This won't do any alpha-blending - it just sets all pixels in the image to the given colour (which may be non-opaque if the image has an alpha channel). */ void clear (const Rectangle& area, Colour colourToClearTo = Colour (0x00000000)); /** Returns a rescaled version of this image. A new image is returned which is a copy of this one, rescaled to the given size. Note that if the new size is identical to the existing image, this will just return a reference to the original image, and won't actually create a duplicate. */ Image rescaled (int newWidth, int newHeight, Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const; /** Creates a copy of this image. Note that it's usually more efficient to use duplicateIfShared(), because it may not be necessary to copy an image if nothing else is using it. @see getReferenceCount */ Image createCopy() const; /** Returns a version of this image with a different image format. A new image is returned which has been converted to the specified format. Note that if the new format is no different to the current one, this will just return a reference to the original image, and won't actually create a copy. */ Image convertedToFormat (PixelFormat newFormat) const; /** Makes sure that no other Image objects share the same underlying data as this one. If no other Image objects refer to the same shared data as this one, this method has no effect. But if there are other references to the data, this will create a new copy of the data internally. Call this if you want to draw onto the image, but want to make sure that this doesn't affect any other code that may be sharing the same data. @see getReferenceCount */ void duplicateIfShared(); /** Returns an image which refers to a subsection of this image. This will not make a copy of the original - the new image will keep a reference to it, so that if the original image is changed, the contents of the subsection will also change. Likewise if you draw into the subimage, you'll also be drawing onto that area of the original image. Note that if you use operator= to make the original Image object refer to something else, the subsection image won't pick up this change, it'll remain pointing at the original. The area passed-in will be clipped to the bounds of this image, so this may return a smaller image than the area you asked for, or even a null image if the area was out-of-bounds. */ Image getClippedImage (const Rectangle& area) const; //============================================================================== /** Returns the colour of one of the pixels in the image. If the coordinates given are beyond the image's boundaries, this will return Colours::transparentBlack. @see setPixelAt, Image::BitmapData::getPixelColour */ Colour getPixelAt (int x, int y) const; /** Sets the colour of one of the image's pixels. If the coordinates are beyond the image's boundaries, then nothing will happen. Note that this won't do any alpha-blending, it'll just replace the existing pixel with the given one. The colour's opacity will be ignored if this image doesn't have an alpha-channel. @see getPixelAt, Image::BitmapData::setPixelColour */ void setPixelAt (int x, int y, Colour colour); /** Changes the opacity of a pixel. This only has an effect if the image has an alpha channel and if the given coordinates are inside the image's boundary. The multiplier must be in the range 0 to 1.0, and the current alpha at the given coordinates will be multiplied by this value. @see setPixelAt */ void multiplyAlphaAt (int x, int y, float multiplier); /** Changes the overall opacity of the image. This will multiply the alpha value of each pixel in the image by the given amount (limiting the resulting alpha values between 0 and 255). This allows you to make an image more or less transparent. If the image doesn't have an alpha channel, this won't have any effect. */ void multiplyAllAlphas (float amountToMultiplyBy); /** Changes all the colours to be shades of grey, based on their current luminosity. */ void desaturate(); //============================================================================== /** Retrieves a section of an image as raw pixel data, so it can be read or written to. You should only use this class as a last resort - messing about with the internals of an image is only recommended for people who really know what they're doing! A BitmapData object should be used as a temporary, stack-based object. Don't keep one hanging around while the image is being used elsewhere. Depending on the way the image class is implemented, this may create a temporary buffer which is copied back to the image when the object is deleted, or it may just get a pointer directly into the image's raw data. You can use the stride and data values in this class directly, but don't alter them! The actual format of the pixel data depends on the image's format - see Image::getFormat(), and the PixelRGB, PixelARGB and PixelAlpha classes for more info. */ class JUCE_API BitmapData { public: enum ReadWriteMode { readOnly, writeOnly, readWrite }; BitmapData (Image& image, int x, int y, int w, int h, ReadWriteMode mode); BitmapData (const Image& image, int x, int y, int w, int h); BitmapData (const Image& image, ReadWriteMode mode); ~BitmapData(); /** Returns a pointer to the start of a line in the image. The coordinate you provide here isn't checked, so it's the caller's responsibility to make sure it's not out-of-range. */ inline uint8* getLinePointer (int y) const noexcept { return data + y * lineStride; } /** Returns a pointer to a pixel in the image. The coordinates you give here are not checked, so it's the caller's responsibility to make sure they're not out-of-range. */ inline uint8* getPixelPointer (int x, int y) const noexcept { return data + y * lineStride + x * pixelStride; } /** Returns the colour of a given pixel. For performance reasons, this won't do any bounds-checking on the coordinates, so it's the caller's repsonsibility to make sure they're within the image's size. */ Colour getPixelColour (int x, int y) const noexcept; /** Sets the colour of a given pixel. For performance reasons, this won't do any bounds-checking on the coordinates, so it's the caller's repsonsibility to make sure they're within the image's size. */ void setPixelColour (int x, int y, Colour colour) const noexcept; /** Returns the size of the bitmap. */ Rectangle getBounds() const noexcept { return Rectangle (width, height); } uint8* data; /**< The raw pixel data, packed according to the image's pixel format. */ PixelFormat pixelFormat; /**< The format of the data. */ int lineStride; /**< The number of bytes between each line. */ int pixelStride; /**< The number of bytes between each pixel. */ int width, height; //============================================================================== /** Used internally by custom image types to manage pixel data lifetime. */ class BitmapDataReleaser { protected: BitmapDataReleaser() {} public: virtual ~BitmapDataReleaser() {} }; ScopedPointer dataReleaser; private: JUCE_DECLARE_NON_COPYABLE (BitmapData) }; //============================================================================== /** Copies a section of the image to somewhere else within itself. */ void moveImageSection (int destX, int destY, int sourceX, int sourceY, int width, int height); /** Creates a RectangleList containing rectangles for all non-transparent pixels of the image. @param result the list that will have the area added to it @param alphaThreshold for a semi-transparent image, any pixels whose alpha is above this level will be considered opaque */ void createSolidAreaMask (RectangleList& result, float alphaThreshold) const; //============================================================================== /** Returns a NamedValueSet that is attached to the image and which can be used for associating custom values with it. If this is a null image, this will return a null pointer. */ NamedValueSet* getProperties() const; //============================================================================== /** Creates a context suitable for drawing onto this image. Don't call this method directly! It's used internally by the Graphics class. */ LowLevelGraphicsContext* createLowLevelContext() const; /** Returns the number of Image objects which are currently referring to the same internal shared image data. @see duplicateIfShared */ int getReferenceCount() const noexcept; //============================================================================== /** @internal */ ImagePixelData* getPixelData() const noexcept { return image; } /** @internal */ explicit Image (ImagePixelData*) noexcept; private: //============================================================================== ReferenceCountedObjectPtr image; JUCE_LEAK_DETECTOR (Image) }; //============================================================================== /** This is a base class for holding image data in implementation-specific ways. You may never need to use this class directly - it's used internally by the Image class to store the actual image data. To access pixel data directly, you should use Image::BitmapData rather than this class. ImagePixelData objects are created indirectly, by subclasses of ImageType. @see Image, ImageType */ class JUCE_API ImagePixelData : public ReferenceCountedObject { public: ImagePixelData (Image::PixelFormat, int width, int height); ~ImagePixelData(); /** Creates a context that will draw into this image. */ virtual LowLevelGraphicsContext* createLowLevelContext() = 0; /** Creates a copy of this image. */ virtual ImagePixelData* clone() = 0; /** Creates an instance of the type of this image. */ virtual ImageType* createType() const = 0; /** Initialises a BitmapData object. */ virtual void initialiseBitmapData (Image::BitmapData&, int x, int y, Image::BitmapData::ReadWriteMode) = 0; /** Returns the number of Image objects which are currently referring to the same internal shared image data. This is different to the reference count as an instance of ImagePixelData can internally depend on another ImagePixelData via it's member variables. */ virtual int getSharedCount() const noexcept; /** The pixel format of the image data. */ const Image::PixelFormat pixelFormat; const int width, height; /** User-defined settings that are attached to this image. @see Image::getProperties(). */ NamedValueSet userData; typedef ReferenceCountedObjectPtr Ptr; //============================================================================== struct Listener { virtual ~Listener() {} virtual void imageDataChanged (ImagePixelData*) = 0; virtual void imageDataBeingDeleted (ImagePixelData*) = 0; }; ListenerList listeners; void sendDataChangeMessage(); private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData) }; //============================================================================== /** This base class is for handlers that control a type of image manipulation format, e.g. an in-memory bitmap, an OpenGL image, CoreGraphics image, etc. @see SoftwareImageType, NativeImageType, OpenGLImageType */ class JUCE_API ImageType { public: ImageType(); virtual ~ImageType(); /** Creates a new image of this type, and the specified parameters. */ virtual ImagePixelData::Ptr create (Image::PixelFormat format, int width, int height, bool shouldClearImage) const = 0; /** Must return a unique number to identify this type. */ virtual int getTypeID() const = 0; /** Returns an image which is a copy of the source image, but using this type of storage mechanism. For example, to make sure that an image is stored in-memory, you could use: @code myImage = SoftwareImageType().convert (myImage); @endcode */ virtual Image convert (const Image& source) const; }; //============================================================================== /** An image storage type which holds the pixels in-memory as a simple block of values. @see ImageType, NativeImageType */ class JUCE_API SoftwareImageType : public ImageType { public: SoftwareImageType(); ~SoftwareImageType(); ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool clearImage) const override; int getTypeID() const override; }; //============================================================================== /** An image storage type which holds the pixels using whatever is the default storage format on the current platform. @see ImageType, SoftwareImageType */ class JUCE_API NativeImageType : public ImageType { public: NativeImageType(); ~NativeImageType(); ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool clearImage) const override; int getTypeID() const override; }; #endif // JUCE_IMAGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp000066400000000000000000000112251320201440200313400ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class ImageCache::Pimpl : private Timer, private DeletedAtShutdown { public: Pimpl() : cacheTimeout (5000) { } ~Pimpl() { clearSingletonInstance(); } Image getFromHashCode (const int64 hashCode) { const ScopedLock sl (lock); for (int i = images.size(); --i >= 0;) { const Item* const item = images.getUnchecked(i); if (item->hashCode == hashCode) return item->image; } return Image::null; } void addImageToCache (const Image& image, const int64 hashCode) { if (image.isValid()) { if (! isTimerRunning()) startTimer (2000); Item* const item = new Item(); item->hashCode = hashCode; item->image = image; item->lastUseTime = Time::getApproximateMillisecondCounter(); const ScopedLock sl (lock); images.add (item); } } void timerCallback() override { const uint32 now = Time::getApproximateMillisecondCounter(); const ScopedLock sl (lock); for (int i = images.size(); --i >= 0;) { Item* const item = images.getUnchecked(i); if (item->image.getReferenceCount() <= 1) { if (now > item->lastUseTime + cacheTimeout || now < item->lastUseTime - 1000) images.remove (i); } else { item->lastUseTime = now; // multiply-referenced, so this image is still in use. } } if (images.size() == 0) stopTimer(); } void releaseUnusedImages() { const ScopedLock sl (lock); for (int i = images.size(); --i >= 0;) if (images.getUnchecked(i)->image.getReferenceCount() <= 1) images.remove (i); } struct Item { Image image; int64 hashCode; uint32 lastUseTime; }; unsigned int cacheTimeout; juce_DeclareSingleton_SingleThreaded_Minimal (ImageCache::Pimpl) private: OwnedArray images; CriticalSection lock; JUCE_DECLARE_NON_COPYABLE (Pimpl) }; juce_ImplementSingleton_SingleThreaded (ImageCache::Pimpl) //============================================================================== Image ImageCache::getFromHashCode (const int64 hashCode) { if (Pimpl::getInstanceWithoutCreating() != nullptr) return Pimpl::getInstanceWithoutCreating()->getFromHashCode (hashCode); return Image::null; } void ImageCache::addImageToCache (const Image& image, const int64 hashCode) { Pimpl::getInstance()->addImageToCache (image, hashCode); } Image ImageCache::getFromFile (const File& file) { const int64 hashCode = file.hashCode64(); Image image (getFromHashCode (hashCode)); if (image.isNull()) { image = ImageFileFormat::loadFrom (file); addImageToCache (image, hashCode); } return image; } Image ImageCache::getFromMemory (const void* imageData, const int dataSize) { const int64 hashCode = (int64) (pointer_sized_int) imageData; Image image (getFromHashCode (hashCode)); if (image.isNull()) { image = ImageFileFormat::loadFrom (imageData, (size_t) dataSize); addImageToCache (image, hashCode); } return image; } void ImageCache::setCacheTimeout (const int millisecs) { jassert (millisecs >= 0); Pimpl::getInstance()->cacheTimeout = (unsigned int) millisecs; } void ImageCache::releaseUnusedImages() { Pimpl::getInstance()->releaseUnusedImages(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.h000066400000000000000000000117351320201440200310130ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IMAGECACHE_H_INCLUDED #define JUCE_IMAGECACHE_H_INCLUDED //============================================================================== /** A global cache of images that have been loaded from files or memory. If you're loading an image and may need to use the image in more than one place, this is used to allow the same image to be shared rather than loading multiple copies into memory. Another advantage is that after images are released, they will be kept in memory for a few seconds before it is actually deleted, so if you're repeatedly loading/deleting the same image, it'll reduce the chances of having to reload it each time. @see Image, ImageFileFormat */ class JUCE_API ImageCache { public: //============================================================================== /** Loads an image from a file, (or just returns the image if it's already cached). If the cache already contains an image that was loaded from this file, that image will be returned. Otherwise, this method will try to load the file, add it to the cache, and return it. Remember that the image returned is shared, so drawing into it might affect other things that are using it! If you want to draw on it, first call Image::duplicateIfShared() @param file the file to try to load @returns the image, or null if it there was an error loading it @see getFromMemory, getFromCache, ImageFileFormat::loadFrom */ static Image getFromFile (const File& file); /** Loads an image from an in-memory image file, (or just returns the image if it's already cached). If the cache already contains an image that was loaded from this block of memory, that image will be returned. Otherwise, this method will try to load the file, add it to the cache, and return it. Remember that the image returned is shared, so drawing into it might affect other things that are using it! If you want to draw on it, first call Image::duplicateIfShared() @param imageData the block of memory containing the image data @param dataSize the data size in bytes @returns the image, or an invalid image if it there was an error loading it @see getFromMemory, getFromCache, ImageFileFormat::loadFrom */ static Image getFromMemory (const void* imageData, int dataSize); //============================================================================== /** Checks the cache for an image with a particular hashcode. If there's an image in the cache with this hashcode, it will be returned, otherwise it will return an invalid image. @param hashCode the hash code that was associated with the image by addImageToCache() @see addImageToCache */ static Image getFromHashCode (int64 hashCode); /** Adds an image to the cache with a user-defined hash-code. The image passed-in will be referenced (not copied) by the cache, so it's probably a good idea not to draw into it after adding it, otherwise this will affect all instances of it that may be in use. @param image the image to add @param hashCode the hash-code to associate with it @see getFromHashCode */ static void addImageToCache (const Image& image, int64 hashCode); /** Changes the amount of time before an unused image will be removed from the cache. By default this is about 5 seconds. */ static void setCacheTimeout (int millisecs); /** Releases any images in the cache that aren't being referenced by active Image objects. */ static void releaseUnusedImages(); private: //============================================================================== class Pimpl; friend class Pimpl; ImageCache(); ~ImageCache(); JUCE_DECLARE_NON_COPYABLE (ImageCache) }; #endif // JUCE_IMAGECACHE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp000066400000000000000000000217141320201440200340210ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ImageConvolutionKernel::ImageConvolutionKernel (const int size_) : values ((size_t) (size_ * size_)), size (size_) { clear(); } ImageConvolutionKernel::~ImageConvolutionKernel() { } //============================================================================== float ImageConvolutionKernel::getKernelValue (const int x, const int y) const noexcept { if (isPositiveAndBelow (x, size) && isPositiveAndBelow (y, size)) return values [x + y * size]; jassertfalse; return 0; } void ImageConvolutionKernel::setKernelValue (const int x, const int y, const float value) noexcept { if (isPositiveAndBelow (x, size) && isPositiveAndBelow (y, size)) { values [x + y * size] = value; } else { jassertfalse; } } void ImageConvolutionKernel::clear() { for (int i = size * size; --i >= 0;) values[i] = 0; } void ImageConvolutionKernel::setOverallSum (const float desiredTotalSum) { double currentTotal = 0.0; for (int i = size * size; --i >= 0;) currentTotal += values[i]; rescaleAllValues ((float) (desiredTotalSum / currentTotal)); } void ImageConvolutionKernel::rescaleAllValues (const float multiplier) { for (int i = size * size; --i >= 0;) values[i] *= multiplier; } //============================================================================== void ImageConvolutionKernel::createGaussianBlur (const float radius) { const double radiusFactor = -1.0 / (radius * radius * 2); const int centre = size >> 1; for (int y = size; --y >= 0;) { for (int x = size; --x >= 0;) { const int cx = x - centre; const int cy = y - centre; values [x + y * size] = (float) exp (radiusFactor * (cx * cx + cy * cy)); } } setOverallSum (1.0f); } //============================================================================== void ImageConvolutionKernel::applyToImage (Image& destImage, const Image& sourceImage, const Rectangle& destinationArea) const { if (sourceImage == destImage) { destImage.duplicateIfShared(); } else { if (sourceImage.getWidth() != destImage.getWidth() || sourceImage.getHeight() != destImage.getHeight() || sourceImage.getFormat() != destImage.getFormat()) { jassertfalse; return; } } const Rectangle area (destinationArea.getIntersection (destImage.getBounds())); if (area.isEmpty()) return; const int right = area.getRight(); const int bottom = area.getBottom(); const Image::BitmapData destData (destImage, area.getX(), area.getY(), area.getWidth(), area.getHeight(), Image::BitmapData::writeOnly); uint8* line = destData.data; const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly); if (destData.pixelStride == 4) { for (int y = area.getY(); y < bottom; ++y) { uint8* dest = line; line += destData.lineStride; for (int x = area.getX(); x < right; ++x) { float c1 = 0; float c2 = 0; float c3 = 0; float c4 = 0; for (int yy = 0; yy < size; ++yy) { const int sy = y + yy - (size >> 1); if (sy >= srcData.height) break; if (sy >= 0) { int sx = x - (size >> 1); const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { if (sx >= srcData.width) break; if (sx >= 0) { const float kernelMult = values [xx + yy * size]; c1 += kernelMult * *src++; c2 += kernelMult * *src++; c3 += kernelMult * *src++; c4 += kernelMult * *src++; } else { src += 4; } ++sx; } } } *dest++ = (uint8) jmin (0xff, roundToInt (c1)); *dest++ = (uint8) jmin (0xff, roundToInt (c2)); *dest++ = (uint8) jmin (0xff, roundToInt (c3)); *dest++ = (uint8) jmin (0xff, roundToInt (c4)); } } } else if (destData.pixelStride == 3) { for (int y = area.getY(); y < bottom; ++y) { uint8* dest = line; line += destData.lineStride; for (int x = area.getX(); x < right; ++x) { float c1 = 0; float c2 = 0; float c3 = 0; for (int yy = 0; yy < size; ++yy) { const int sy = y + yy - (size >> 1); if (sy >= srcData.height) break; if (sy >= 0) { int sx = x - (size >> 1); const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { if (sx >= srcData.width) break; if (sx >= 0) { const float kernelMult = values [xx + yy * size]; c1 += kernelMult * *src++; c2 += kernelMult * *src++; c3 += kernelMult * *src++; } else { src += 3; } ++sx; } } } *dest++ = (uint8) roundToInt (c1); *dest++ = (uint8) roundToInt (c2); *dest++ = (uint8) roundToInt (c3); } } } else if (destData.pixelStride == 1) { for (int y = area.getY(); y < bottom; ++y) { uint8* dest = line; line += destData.lineStride; for (int x = area.getX(); x < right; ++x) { float c1 = 0; for (int yy = 0; yy < size; ++yy) { const int sy = y + yy - (size >> 1); if (sy >= srcData.height) break; if (sy >= 0) { int sx = x - (size >> 1); const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { if (sx >= srcData.width) break; if (sx >= 0) { const float kernelMult = values [xx + yy * size]; c1 += kernelMult * *src++; } else { src += 3; } ++sx; } } } *dest++ = (uint8) roundToInt (c1); } } } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.h000066400000000000000000000101301320201440200334540ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IMAGECONVOLUTIONKERNEL_H_INCLUDED #define JUCE_IMAGECONVOLUTIONKERNEL_H_INCLUDED //============================================================================== /** Represents a filter kernel to use in convoluting an image. @see Image::applyConvolution */ class JUCE_API ImageConvolutionKernel { public: //============================================================================== /** Creates an empty convulution kernel. @param size the length of each dimension of the kernel, so e.g. if the size is 5, it will create a 5x5 kernel */ ImageConvolutionKernel (int size); /** Destructor. */ ~ImageConvolutionKernel(); //============================================================================== /** Resets all values in the kernel to zero. */ void clear(); /** Returns one of the kernel values. */ float getKernelValue (int x, int y) const noexcept; /** Sets the value of a specific cell in the kernel. The x and y parameters must be in the range 0 < x < getKernelSize(). @see setOverallSum */ void setKernelValue (int x, int y, float value) noexcept; /** Rescales all values in the kernel to make the total add up to a fixed value. This will multiply all values in the kernel by (desiredTotalSum / currentTotalSum). */ void setOverallSum (float desiredTotalSum); /** Multiplies all values in the kernel by a value. */ void rescaleAllValues (float multiplier); /** Intialises the kernel for a gaussian blur. @param blurRadius this may be larger or smaller than the kernel's actual size but this will obviously be wasteful or clip at the edges. Ideally the kernel should be just larger than (blurRadius * 2). */ void createGaussianBlur (float blurRadius); //============================================================================== /** Returns the size of the kernel. E.g. if it's a 3x3 kernel, this returns 3. */ int getKernelSize() const { return size; } //============================================================================== /** Applies the kernel to an image. @param destImage the image that will receive the resultant convoluted pixels. @param sourceImage the source image to read from - this can be the same image as the destination, but if different, it must be exactly the same size and format. @param destinationArea the region of the image to apply the filter to */ void applyToImage (Image& destImage, const Image& sourceImage, const Rectangle& destinationArea) const; private: //============================================================================== HeapBlock values; const int size; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageConvolutionKernel) }; #endif // JUCE_IMAGECONVOLUTIONKERNEL_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.cpp000066400000000000000000000055041320201440200323700ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct DefaultImageFormats { static ImageFileFormat** get() { static DefaultImageFormats formats; return formats.formats; } private: DefaultImageFormats() noexcept { formats[0] = &png; formats[1] = &jpg; formats[2] = &gif; formats[3] = nullptr; } PNGImageFormat png; JPEGImageFormat jpg; GIFImageFormat gif; ImageFileFormat* formats[4]; }; ImageFileFormat* ImageFileFormat::findImageFormatForStream (InputStream& input) { const int64 streamPos = input.getPosition(); for (ImageFileFormat** i = DefaultImageFormats::get(); *i != nullptr; ++i) { const bool found = (*i)->canUnderstand (input); input.setPosition (streamPos); if (found) return *i; } return nullptr; } ImageFileFormat* ImageFileFormat::findImageFormatForFileExtension (const File& file) { for (ImageFileFormat** i = DefaultImageFormats::get(); *i != nullptr; ++i) if ((*i)->usesFileExtension (file)) return *i; return nullptr; } //============================================================================== Image ImageFileFormat::loadFrom (InputStream& input) { ImageFileFormat* const format = findImageFormatForStream (input); if (format != nullptr) return format->decodeImage (input); return Image::null; } Image ImageFileFormat::loadFrom (const File& file) { FileInputStream stream (file); if (stream.openedOk()) { BufferedInputStream b (stream, 8192); return loadFrom (b); } return Image::null; } Image ImageFileFormat::loadFrom (const void* rawData, const size_t numBytes) { if (rawData != nullptr && numBytes > 4) { MemoryInputStream stream (rawData, numBytes, false); return loadFrom (stream); } return Image::null; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.h000066400000000000000000000200711320201440200320310ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IMAGEFILEFORMAT_H_INCLUDED #define JUCE_IMAGEFILEFORMAT_H_INCLUDED //============================================================================== /** Base-class for codecs that can read and write image file formats such as PNG, JPEG, etc. This class also contains static methods to make it easy to load images from files, streams or from memory. @see Image, ImageCache */ class JUCE_API ImageFileFormat { protected: //============================================================================== /** Creates an ImageFormat. */ ImageFileFormat() {} public: /** Destructor. */ virtual ~ImageFileFormat() {} //============================================================================== /** Returns a description of this file format. E.g. "JPEG", "PNG" */ virtual String getFormatName() = 0; /** Returns true if the given stream seems to contain data that this format understands. The format class should only read the first few bytes of the stream and sniff for header bytes that it understands. Note that this will advance the stream and leave it in a new position, so if you're planning on re-using it, you may want to rewind it after calling this method. @see decodeImage */ virtual bool canUnderstand (InputStream& input) = 0; /** Returns true if this format uses the file extension of the given file. */ virtual bool usesFileExtension (const File& possibleFile) = 0; /** Tries to decode and return an image from the given stream. This will be called for an image format after calling its canUnderStand() method to see if it can handle the stream. @param input the stream to read the data from. The stream will be positioned at the start of the image data (but this may not necessarily be position 0) @returns the image that was decoded, or an invalid image if it fails. @see loadFrom */ virtual Image decodeImage (InputStream& input) = 0; //============================================================================== /** Attempts to write an image to a stream. To specify extra information like encoding quality, there will be appropriate parameters in the subclasses of the specific file types. @returns true if it nothing went wrong. */ virtual bool writeImageToStream (const Image& sourceImage, OutputStream& destStream) = 0; //============================================================================== /** Tries the built-in formats to see if it can find one to read this stream. There are currently built-in decoders for PNG, JPEG and GIF formats. The object that is returned should not be deleted by the caller. @see canUnderstand, decodeImage, loadFrom */ static ImageFileFormat* findImageFormatForStream (InputStream& input); /** Looks for a format that can handle the given file extension. There are currently built-in formats for PNG, JPEG and GIF formats. The object that is returned should not be deleted by the caller. */ static ImageFileFormat* findImageFormatForFileExtension (const File& file); //============================================================================== /** Tries to load an image from a stream. This will use the findImageFormatForStream() method to locate a suitable codec, and use that to load the image. @returns the image that was decoded, or an invalid image if it fails. */ static Image loadFrom (InputStream& input); /** Tries to load an image from a file. This will use the findImageFormatForStream() method to locate a suitable codec, and use that to load the image. @returns the image that was decoded, or an invalid image if it fails. */ static Image loadFrom (const File& file); /** Tries to load an image from a block of raw image data. This will use the findImageFormatForStream() method to locate a suitable codec, and use that to load the image. @returns the image that was decoded, or an invalid image if it fails. */ static Image loadFrom (const void* rawData, size_t numBytesOfData); }; //============================================================================== /** A subclass of ImageFileFormat for reading and writing PNG files. @see ImageFileFormat, JPEGImageFormat */ class JUCE_API PNGImageFormat : public ImageFileFormat { public: //============================================================================== PNGImageFormat(); ~PNGImageFormat(); //============================================================================== String getFormatName() override; bool usesFileExtension (const File&) override; bool canUnderstand (InputStream&) override; Image decodeImage (InputStream&) override; bool writeImageToStream (const Image&, OutputStream&) override; }; //============================================================================== /** A subclass of ImageFileFormat for reading and writing JPEG files. @see ImageFileFormat, PNGImageFormat */ class JUCE_API JPEGImageFormat : public ImageFileFormat { public: //============================================================================== JPEGImageFormat(); ~JPEGImageFormat(); //============================================================================== /** Specifies the quality to be used when writing a JPEG file. @param newQuality a value 0 to 1.0, where 0 is low quality, 1.0 is best, or any negative value is "default" quality */ void setQuality (float newQuality); //============================================================================== String getFormatName() override; bool usesFileExtension (const File&) override; bool canUnderstand (InputStream&) override; Image decodeImage (InputStream&) override; bool writeImageToStream (const Image&, OutputStream&) override; private: float quality; }; //============================================================================== /** A subclass of ImageFileFormat for reading GIF files. @see ImageFileFormat, PNGImageFormat, JPEGImageFormat */ class JUCE_API GIFImageFormat : public ImageFileFormat { public: //============================================================================== GIFImageFormat(); ~GIFImageFormat(); //============================================================================== String getFormatName() override; bool usesFileExtension (const File&) override; bool canUnderstand (InputStream&) override; Image decodeImage (InputStream&) override; bool writeImageToStream (const Image&, OutputStream&) override; }; #endif // JUCE_IMAGEFILEFORMAT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp000066400000000000000000000130071320201440200277250ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if defined (JUCE_GRAPHICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix header files that the compiler may be using. */ #error "Incorrect use of JUCE cpp file" #endif // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_graphics.h" //============================================================================== #if JUCE_MAC #import #elif JUCE_WINDOWS #if JUCE_MINGW && JUCE_USE_DIRECTWRITE #warning "DirectWrite not currently implemented with mingw..." #undef JUCE_USE_DIRECTWRITE #endif #if JUCE_USE_DIRECTWRITE /* If you hit a compile error trying to include these files, you may need to update your version of the Windows SDK to the latest one. The DirectWrite and Direct2D headers are in the version 7 SDKs. */ #include #include #endif #if JUCE_MINGW #include #endif #elif JUCE_IOS #import #import #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2 #error "JUCE no longer supports targets earlier than iOS 3.2" #endif #elif JUCE_LINUX #ifndef JUCE_USE_FREETYPE #define JUCE_USE_FREETYPE 1 #endif #endif #if JUCE_USE_FREETYPE #if JUCE_USE_FREETYPE_AMALGAMATED #include "native/freetype/FreeTypeAmalgam.h" #else #include #include FT_FREETYPE_H #endif #endif #undef SIZEOF #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER #define JUCE_USING_COREIMAGE_LOADER 1 #else #define JUCE_USING_COREIMAGE_LOADER 0 #endif //============================================================================== namespace juce { #include "colour/juce_Colour.cpp" #include "colour/juce_ColourGradient.cpp" #include "colour/juce_Colours.cpp" #include "colour/juce_FillType.cpp" #include "geometry/juce_AffineTransform.cpp" #include "geometry/juce_EdgeTable.cpp" #include "geometry/juce_Path.cpp" #include "geometry/juce_PathIterator.cpp" #include "geometry/juce_PathStrokeType.cpp" #include "placement/juce_RectanglePlacement.cpp" #include "contexts/juce_GraphicsContext.cpp" #include "contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp" #include "contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp" #include "images/juce_Image.cpp" #include "images/juce_ImageCache.cpp" #include "images/juce_ImageConvolutionKernel.cpp" #include "images/juce_ImageFileFormat.cpp" #include "image_formats/juce_GIFLoader.cpp" #include "image_formats/juce_JPEGLoader.cpp" #include "image_formats/juce_PNGLoader.cpp" #include "fonts/juce_AttributedString.cpp" #include "fonts/juce_Typeface.cpp" #include "fonts/juce_CustomTypeface.cpp" #include "fonts/juce_Font.cpp" #include "fonts/juce_GlyphArrangement.cpp" #include "fonts/juce_TextLayout.cpp" #include "effects/juce_DropShadowEffect.cpp" #include "effects/juce_GlowEffect.cpp" #if JUCE_USE_FREETYPE #include "native/juce_freetype_Fonts.cpp" #endif //============================================================================== #if JUCE_MAC || JUCE_IOS #include "../juce_core/native/juce_osx_ObjCHelpers.h" #include "native/juce_mac_CoreGraphicsHelpers.h" #include "native/juce_mac_Fonts.mm" #include "native/juce_mac_CoreGraphicsContext.mm" #elif JUCE_WINDOWS #include "../juce_core/native/juce_win32_ComSmartPtr.h" #include "native/juce_win32_DirectWriteTypeface.cpp" #include "native/juce_win32_DirectWriteTypeLayout.cpp" #include "native/juce_win32_Fonts.cpp" #if JUCE_DIRECT2D #include "native/juce_win32_Direct2DGraphicsContext.cpp" #endif #elif JUCE_LINUX #include "native/juce_linux_Fonts.cpp" #elif JUCE_ANDROID #include "../juce_core/native/juce_android_JNIHelpers.h" #include "native/juce_android_GraphicsContext.cpp" #include "native/juce_android_Fonts.cpp" #endif } //============================================================================== #if JUCE_USE_FREETYPE && JUCE_USE_FREETYPE_AMALGAMATED #undef PIXEL_MASK #undef ZLIB_VERSION #undef Z_ASCII #undef ZEXTERN #undef ZEXPORT extern "C" { #include "native/freetype/FreeTypeAmalgam.c" } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/juce_graphics.h000066400000000000000000000071571320201440200274030ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_GRAPHICS_H_INCLUDED // %% #define JUCE_GRAPHICS_H_INCLUDED #include "../juce_core/juce_core.h" #include "../juce_events/juce_events.h" //============================================================================= /** Config: JUCE_USE_COREIMAGE_LOADER On OSX, enabling this flag means that the CoreImage codecs will be used to load PNG/JPEG/GIF files. It is enabled by default, but you may want to disable it if you'd rather use libpng, libjpeg, etc. */ #ifndef JUCE_USE_COREIMAGE_LOADER #define JUCE_USE_COREIMAGE_LOADER 1 #endif /** Config: JUCE_USE_DIRECTWRITE Enabling this flag means that DirectWrite will be used when available for font management and layout. */ #ifndef JUCE_USE_DIRECTWRITE #define JUCE_USE_DIRECTWRITE 1 #endif #ifndef JUCE_INCLUDE_PNGLIB_CODE #define JUCE_INCLUDE_PNGLIB_CODE 1 #endif #ifndef JUCE_INCLUDE_JPEGLIB_CODE #define JUCE_INCLUDE_JPEGLIB_CODE 1 #endif #ifndef USE_COREGRAPHICS_RENDERING #define USE_COREGRAPHICS_RENDERING 1 #endif //============================================================================= namespace juce { class Image; class AffineTransform; class Path; class Font; class Graphics; class FillType; class LowLevelGraphicsContext; #include "geometry/juce_AffineTransform.h" #include "geometry/juce_Point.h" #include "geometry/juce_Line.h" #include "geometry/juce_Rectangle.h" #include "placement/juce_Justification.h" #include "geometry/juce_Path.h" #include "geometry/juce_RectangleList.h" #include "colour/juce_PixelFormats.h" #include "colour/juce_Colour.h" #include "colour/juce_ColourGradient.h" #include "colour/juce_Colours.h" #include "geometry/juce_BorderSize.h" #include "geometry/juce_EdgeTable.h" #include "geometry/juce_PathIterator.h" #include "geometry/juce_PathStrokeType.h" #include "placement/juce_RectanglePlacement.h" #include "images/juce_ImageCache.h" #include "images/juce_ImageConvolutionKernel.h" #include "images/juce_ImageFileFormat.h" #include "fonts/juce_AttributedString.h" #include "fonts/juce_Typeface.h" #include "fonts/juce_Font.h" #include "fonts/juce_GlyphArrangement.h" #include "fonts/juce_TextLayout.h" #include "fonts/juce_CustomTypeface.h" #include "contexts/juce_GraphicsContext.h" #include "contexts/juce_LowLevelGraphicsContext.h" #include "images/juce_Image.h" #include "colour/juce_FillType.h" #include "native/juce_RenderingHelpers.h" #include "contexts/juce_LowLevelGraphicsSoftwareRenderer.h" #include "contexts/juce_LowLevelGraphicsPostScriptRenderer.h" #include "effects/juce_ImageEffectFilter.h" #include "effects/juce_DropShadowEffect.h" #include "effects/juce_GlowEffect.h" } #endif // JUCE_GRAPHICS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/juce_graphics.mm000066400000000000000000000017011320201440200275520ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_graphics.cpp" libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/juce_module_info000066400000000000000000000021551320201440200276460ustar00rootroot00000000000000{ "id": "juce_graphics", "name": "JUCE graphics classes", "version": "3.2.0", "description": "Classes for 2D vector graphics, image loading/saving, font handling, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", "dependencies": [ { "id": "juce_core", "version": "matching" }, { "id": "juce_events", "version": "matching" } ], "include": "juce_graphics.h", "compile": [ { "file": "juce_graphics.cpp", "target": "! xcode" }, { "file": "juce_graphics.mm", "target": "xcode" } ], "browse": [ "colour/*", "contexts/*", "images/*", "image_formats/*", "geometry/*", "placement/*", "fonts/*", "effects/*", "native/*" ], "OSXFrameworks": "Cocoa QuartzCore", "iOSFrameworks": "CoreGraphics CoreText QuartzCore", "LinuxLibs": "X11 Xinerama Xext freetype" } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/000077500000000000000000000000001320201440200257005ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h000066400000000000000000003150021320201440200323200ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_RENDERINGHELPERS_H_INCLUDED #define JUCE_RENDERINGHELPERS_H_INCLUDED #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4127) // "expression is constant" warning #endif namespace RenderingHelpers { //============================================================================== /** Holds either a simple integer translation, or an affine transform. */ class TranslationOrTransform { public: TranslationOrTransform (Point origin) noexcept : offset (origin), isOnlyTranslated (true), isRotated (false) { } TranslationOrTransform (const TranslationOrTransform& other) noexcept : complexTransform (other.complexTransform), offset (other.offset), isOnlyTranslated (other.isOnlyTranslated), isRotated (other.isRotated) { } AffineTransform getTransform() const noexcept { return isOnlyTranslated ? AffineTransform::translation (offset) : complexTransform; } AffineTransform getTransformWith (const AffineTransform& userTransform) const noexcept { return isOnlyTranslated ? userTransform.translated (offset) : userTransform.followedBy (complexTransform); } void setOrigin (Point delta) noexcept { if (isOnlyTranslated) offset += delta; else complexTransform = AffineTransform::translation (delta) .followedBy (complexTransform); } void addTransform (const AffineTransform& t) noexcept { if (isOnlyTranslated && t.isOnlyTranslation()) { const int tx = (int) (t.getTranslationX() * 256.0f); const int ty = (int) (t.getTranslationY() * 256.0f); if (((tx | ty) & 0xf8) == 0) { offset += Point (tx >> 8, ty >> 8); return; } } complexTransform = getTransformWith (t); isOnlyTranslated = false; isRotated = (complexTransform.mat01 != 0 || complexTransform.mat10 != 0 || complexTransform.mat00 < 0 || complexTransform.mat11 < 0); } float getPhysicalPixelScaleFactor() const noexcept { return isOnlyTranslated ? 1.0f : std::abs (complexTransform.getScaleFactor()); } void moveOriginInDeviceSpace (Point delta) noexcept { if (isOnlyTranslated) offset += delta; else complexTransform = complexTransform.translated (delta); } Rectangle translated (const Rectangle& r) const noexcept { jassert (isOnlyTranslated); return r + offset; } Rectangle translated (const Rectangle& r) const noexcept { jassert (isOnlyTranslated); return r + offset.toFloat(); } template RectangleOrPoint transformed (const RectangleOrPoint& r) const noexcept { jassert (! isOnlyTranslated); return r.transformedBy (complexTransform); } template Rectangle deviceSpaceToUserSpace (const Rectangle& r) const noexcept { return isOnlyTranslated ? r - offset : r.transformedBy (complexTransform.inverted()); } AffineTransform complexTransform; Point offset; bool isOnlyTranslated, isRotated; }; //============================================================================== /** Holds a cache of recently-used glyph objects of some type. */ template class GlyphCache : private DeletedAtShutdown { public: GlyphCache() { reset(); } ~GlyphCache() { getSingletonPointer() = nullptr; } static GlyphCache& getInstance() { GlyphCache*& g = getSingletonPointer(); if (g == nullptr) g = new GlyphCache(); return *g; } //============================================================================== void reset() { const ScopedLock sl (lock); glyphs.clear(); addNewGlyphSlots (120); hits.set (0); misses.set (0); } void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, Point pos) { if (ReferenceCountedObjectPtr glyph = findOrCreateGlyph (font, glyphNumber)) { glyph->lastAccessCount = ++accessCounter; glyph->draw (target, pos); } } ReferenceCountedObjectPtr findOrCreateGlyph (const Font& font, int glyphNumber) { const ScopedLock sl (lock); if (CachedGlyphType* g = findExistingGlyph (font, glyphNumber)) { ++hits; return g; } ++misses; CachedGlyphType* g = getGlyphForReuse(); jassert (g != nullptr); g->generate (font, glyphNumber); return g; } private: friend struct ContainerDeletePolicy; ReferenceCountedArray glyphs; Atomic accessCounter, hits, misses; CriticalSection lock; CachedGlyphType* findExistingGlyph (const Font& font, int glyphNumber) const { for (int i = 0; i < glyphs.size(); ++i) { CachedGlyphType* const g = glyphs.getUnchecked (i); if (g->glyph == glyphNumber && g->font == font) return g; } return nullptr; } CachedGlyphType* getGlyphForReuse() { if (hits.value + misses.value > glyphs.size() * 16) { if (misses.value * 2 > hits.value) addNewGlyphSlots (32); hits.set (0); misses.set (0); } if (CachedGlyphType* g = findLeastRecentlyUsedGlyph()) return g; addNewGlyphSlots (32); return glyphs.getLast(); } void addNewGlyphSlots (int num) { glyphs.ensureStorageAllocated (glyphs.size() + num); while (--num >= 0) glyphs.add (new CachedGlyphType()); } CachedGlyphType* findLeastRecentlyUsedGlyph() const noexcept { CachedGlyphType* oldest = nullptr; int oldestCounter = std::numeric_limits::max(); for (int i = glyphs.size() - 1; --i >= 0;) { CachedGlyphType* const glyph = glyphs.getUnchecked(i); if (glyph->lastAccessCount <= oldestCounter && glyph->getReferenceCount() == 1) { oldestCounter = glyph->lastAccessCount; oldest = glyph; } } return oldest; } static GlyphCache*& getSingletonPointer() noexcept { static GlyphCache* g = nullptr; return g; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache) }; //============================================================================== /** Caches a glyph as an edge-table. */ template class CachedGlyphEdgeTable : public ReferenceCountedObject { public: CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {} void draw (RendererType& state, Point pos) const { if (snapToIntegerCoordinate) pos.x = std::floor (pos.x + 0.5f); if (edgeTable != nullptr) state.fillEdgeTable (*edgeTable, pos.x, roundToInt (pos.y)); } void generate (const Font& newFont, const int glyphNumber) { font = newFont; Typeface* const typeface = newFont.getTypeface(); snapToIntegerCoordinate = typeface->isHinted(); glyph = glyphNumber; const float fontHeight = font.getHeight(); edgeTable = typeface->getEdgeTableForGlyph (glyphNumber, AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight), fontHeight); } Font font; ScopedPointer edgeTable; int glyph, lastAccessCount; bool snapToIntegerCoordinate; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable) }; //============================================================================== /** Calculates the alpha values and positions for rendering the edges of a non-pixel-aligned rectangle. */ struct FloatRectangleRasterisingInfo { FloatRectangleRasterisingInfo (const Rectangle& area) : left (roundToInt (256.0f * area.getX())), top (roundToInt (256.0f * area.getY())), right (roundToInt (256.0f * area.getRight())), bottom (roundToInt (256.0f * area.getBottom())) { if ((top >> 8) == (bottom >> 8)) { topAlpha = bottom - top; bottomAlpha = 0; totalTop = top >> 8; totalBottom = bottom = top = totalTop + 1; } else { if ((top & 255) == 0) { topAlpha = 0; top = totalTop = (top >> 8); } else { topAlpha = 255 - (top & 255); totalTop = (top >> 8); top = totalTop + 1; } bottomAlpha = bottom & 255; bottom >>= 8; totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0); } if ((left >> 8) == (right >> 8)) { leftAlpha = right - left; rightAlpha = 0; totalLeft = (left >> 8); totalRight = right = left = totalLeft + 1; } else { if ((left & 255) == 0) { leftAlpha = 0; left = totalLeft = (left >> 8); } else { leftAlpha = 255 - (left & 255); totalLeft = (left >> 8); left = totalLeft + 1; } rightAlpha = right & 255; right >>= 8; totalRight = right + (rightAlpha != 0 ? 1 : 0); } } template void iterate (Callback& callback) const { if (topAlpha != 0) callback (totalLeft, totalTop, totalRight - totalLeft, 1, topAlpha); if (bottomAlpha != 0) callback (totalLeft, bottom, totalRight - totalLeft, 1, bottomAlpha); if (leftAlpha != 0) callback (totalLeft, totalTop, 1, totalBottom - totalTop, leftAlpha); if (rightAlpha != 0) callback (right, totalTop, 1, totalBottom - totalTop, rightAlpha); callback (left, top, right - left, bottom - top, 255); } inline bool isOnePixelWide() const noexcept { return right - left == 1 && leftAlpha + rightAlpha == 0; } inline int getTopLeftCornerAlpha() const noexcept { return (topAlpha * leftAlpha) >> 8; } inline int getTopRightCornerAlpha() const noexcept { return (topAlpha * rightAlpha) >> 8; } inline int getBottomLeftCornerAlpha() const noexcept { return (bottomAlpha * leftAlpha) >> 8; } inline int getBottomRightCornerAlpha() const noexcept { return (bottomAlpha * rightAlpha) >> 8; } //============================================================================== int left, top, right, bottom; // bounds of the solid central area, excluding anti-aliased edges int totalTop, totalLeft, totalBottom, totalRight; // bounds of the total area, including edges int topAlpha, leftAlpha, bottomAlpha, rightAlpha; // alpha of each anti-aliased edge }; //============================================================================== /** Contains classes for calculating the colour of pixels within various types of gradient. */ namespace GradientPixelIterators { /** Iterates the colour of pixels in a linear gradient */ class Linear { public: Linear (const ColourGradient& gradient, const AffineTransform& transform, const PixelARGB* const colours, const int numColours) : lookupTable (colours), numEntries (numColours) { jassert (numColours >= 0); Point p1 (gradient.point1); Point p2 (gradient.point2); if (! transform.isIdentity()) { const Line l (p2, p1); Point p3 = l.getPointAlongLine (0.0f, 100.0f); p1.applyTransform (transform); p2.applyTransform (transform); p3.applyTransform (transform); p2 = Line (p2, p3).findNearestPointTo (p1); } vertical = std::abs (p1.x - p2.x) < 0.001f; horizontal = std::abs (p1.y - p2.y) < 0.001f; if (vertical) { scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.y - p1.y)); start = roundToInt (p1.y * (float) scale); } else if (horizontal) { scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.x - p1.x)); start = roundToInt (p1.x * (float) scale); } else { grad = (p2.getY() - p1.y) / (double) (p1.x - p2.x); yTerm = p1.getY() - p1.x / grad; scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (p2.y * grad - p2.x))); grad *= scale; } } forcedinline void setY (const int y) noexcept { if (vertical) linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)]; else if (! horizontal) start = roundToInt ((y - yTerm) * grad); } inline PixelARGB getPixel (const int x) const noexcept { return vertical ? linePix : lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; } private: const PixelARGB* const lookupTable; const int numEntries; PixelARGB linePix; int start, scale; double grad, yTerm; bool vertical, horizontal; enum { numScaleBits = 12 }; JUCE_DECLARE_NON_COPYABLE (Linear) }; //============================================================================== /** Iterates the colour of pixels in a circular radial gradient */ class Radial { public: Radial (const ColourGradient& gradient, const AffineTransform&, const PixelARGB* const colours, const int numColours) : lookupTable (colours), numEntries (numColours), gx1 (gradient.point1.x), gy1 (gradient.point1.y) { jassert (numColours >= 0); const Point diff (gradient.point1 - gradient.point2); maxDist = diff.x * diff.x + diff.y * diff.y; invScale = numEntries / std::sqrt (maxDist); jassert (roundToInt (std::sqrt (maxDist) * invScale) <= numEntries); } forcedinline void setY (const int y) noexcept { dy = y - gy1; dy *= dy; } inline PixelARGB getPixel (const int px) const noexcept { double x = px - gx1; x *= x; x += dy; return lookupTable [x >= maxDist ? numEntries : roundToInt (std::sqrt (x) * invScale)]; } protected: const PixelARGB* const lookupTable; const int numEntries; const double gx1, gy1; double maxDist, invScale, dy; JUCE_DECLARE_NON_COPYABLE (Radial) }; //============================================================================== /** Iterates the colour of pixels in a skewed radial gradient */ class TransformedRadial : public Radial { public: TransformedRadial (const ColourGradient& gradient, const AffineTransform& transform, const PixelARGB* const colours, const int numColours) : Radial (gradient, transform, colours, numColours), inverseTransform (transform.inverted()) { tM10 = inverseTransform.mat10; tM00 = inverseTransform.mat00; } forcedinline void setY (const int y) noexcept { const float floatY = (float) y; lineYM01 = inverseTransform.mat01 * floatY + inverseTransform.mat02 - gx1; lineYM11 = inverseTransform.mat11 * floatY + inverseTransform.mat12 - gy1; } inline PixelARGB getPixel (const int px) const noexcept { double x = px; const double y = tM10 * x + lineYM11; x = tM00 * x + lineYM01; x *= x; x += y * y; if (x >= maxDist) return lookupTable [numEntries]; return lookupTable [jmin (numEntries, roundToInt (std::sqrt (x) * invScale))]; } private: double tM10, tM00, lineYM01, lineYM11; const AffineTransform inverseTransform; JUCE_DECLARE_NON_COPYABLE (TransformedRadial) }; } #define JUCE_PERFORM_PIXEL_OP_LOOP(op) \ { \ const int destStride = destData.pixelStride; \ do { dest->op; dest = addBytesToPointer (dest, destStride); } while (--width > 0); \ } //============================================================================== /** Contains classes for filling edge tables with various fill types. */ namespace EdgeTableFillers { /** Fills an edge-table with a solid colour. */ template class SolidColour { public: SolidColour (const Image::BitmapData& image, const PixelARGB colour) : destData (image), sourceColour (colour) { if (sizeof (PixelType) == 3 && destData.pixelStride == sizeof (PixelType)) { areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen() && sourceColour.getGreen() == sourceColour.getBlue(); filler[0].set (sourceColour); filler[1].set (sourceColour); filler[2].set (sourceColour); filler[3].set (sourceColour); } else { areRGBComponentsEqual = false; } } forcedinline void setEdgeTableYPos (const int y) noexcept { linePixels = (PixelType*) destData.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept { if (replaceExisting) getPixel (x)->set (sourceColour); else getPixel (x)->blend (sourceColour, (uint32) alphaLevel); } forcedinline void handleEdgeTablePixelFull (const int x) const noexcept { if (replaceExisting) getPixel (x)->set (sourceColour); else getPixel (x)->blend (sourceColour); } forcedinline void handleEdgeTableLine (const int x, const int width, const int alphaLevel) const noexcept { PixelARGB p (sourceColour); p.multiplyAlpha (alphaLevel); PixelType* dest = getPixel (x); if (replaceExisting || p.getAlpha() >= 0xff) replaceLine (dest, p, width); else blendLine (dest, p, width); } forcedinline void handleEdgeTableLineFull (const int x, const int width) const noexcept { PixelType* dest = getPixel (x); if (replaceExisting || sourceColour.getAlpha() >= 0xff) replaceLine (dest, sourceColour, width); else blendLine (dest, sourceColour, width); } private: const Image::BitmapData& destData; PixelType* linePixels; PixelARGB sourceColour; PixelRGB filler [4]; bool areRGBComponentsEqual; forcedinline PixelType* getPixel (const int x) const noexcept { return addBytesToPointer (linePixels, x * destData.pixelStride); } inline void blendLine (PixelType* dest, const PixelARGB colour, int width) const noexcept { JUCE_PERFORM_PIXEL_OP_LOOP (blend (colour)) } forcedinline void replaceLine (PixelRGB* dest, const PixelARGB colour, int width) const noexcept { if (destData.pixelStride == sizeof (*dest)) { if (areRGBComponentsEqual) // if all the component values are the same, we can cheat.. { memset (dest, colour.getRed(), (size_t) width * 3); } else { if (width >> 5) { const int* const intFiller = reinterpret_cast (filler); while (width > 8 && (((pointer_sized_int) dest) & 7) != 0) { dest->set (colour); ++dest; --width; } while (width > 4) { int* d = reinterpret_cast (dest); *d++ = intFiller[0]; *d++ = intFiller[1]; *d++ = intFiller[2]; dest = reinterpret_cast (d); width -= 4; } } while (--width >= 0) { dest->set (colour); ++dest; } } } else { JUCE_PERFORM_PIXEL_OP_LOOP (set (colour)) } } forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB colour, int width) const noexcept { if (destData.pixelStride == sizeof (*dest)) memset (dest, colour.getAlpha(), (size_t) width); else JUCE_PERFORM_PIXEL_OP_LOOP (setAlpha (colour.getAlpha())) } forcedinline void replaceLine (PixelARGB* dest, const PixelARGB colour, int width) const noexcept { JUCE_PERFORM_PIXEL_OP_LOOP (set (colour)) } JUCE_DECLARE_NON_COPYABLE (SolidColour) }; //============================================================================== /** Fills an edge-table with a gradient. */ template class Gradient : public GradientType { public: Gradient (const Image::BitmapData& dest, const ColourGradient& gradient, const AffineTransform& transform, const PixelARGB* const colours, const int numColours) : GradientType (gradient, transform, colours, numColours - 1), destData (dest) { } forcedinline void setEdgeTableYPos (const int y) noexcept { linePixels = (PixelType*) destData.getLinePointer (y); GradientType::setY (y); } forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept { getPixel (x)->blend (GradientType::getPixel (x), (uint32) alphaLevel); } forcedinline void handleEdgeTablePixelFull (const int x) const noexcept { getPixel (x)->blend (GradientType::getPixel (x)); } void handleEdgeTableLine (int x, int width, const int alphaLevel) const noexcept { PixelType* dest = getPixel (x); if (alphaLevel < 0xff) JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++), (uint32) alphaLevel)) else JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++))) } void handleEdgeTableLineFull (int x, int width) const noexcept { PixelType* dest = getPixel (x); JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++))) } private: const Image::BitmapData& destData; PixelType* linePixels; forcedinline PixelType* getPixel (const int x) const noexcept { return addBytesToPointer (linePixels, x * destData.pixelStride); } JUCE_DECLARE_NON_COPYABLE (Gradient) }; //============================================================================== /** Fills an edge-table with a non-transformed image. */ template class ImageFill { public: ImageFill (const Image::BitmapData& dest, const Image::BitmapData& src, const int alpha, const int x, const int y) : destData (dest), srcData (src), extraAlpha (alpha + 1), xOffset (repeatPattern ? negativeAwareModulo (x, src.width) - src.width : x), yOffset (repeatPattern ? negativeAwareModulo (y, src.height) - src.height : y) { } forcedinline void setEdgeTableYPos (int y) noexcept { linePixels = (DestPixelType*) destData.getLinePointer (y); y -= yOffset; if (repeatPattern) { jassert (y >= 0); y %= srcData.height; } sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const noexcept { alphaLevel = (alphaLevel * extraAlpha) >> 8; getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) alphaLevel); } forcedinline void handleEdgeTablePixelFull (const int x) const noexcept { getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) extraAlpha); } void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept { DestPixelType* dest = getDestPixel (x); alphaLevel = (alphaLevel * extraAlpha) >> 8; x -= xOffset; if (repeatPattern) { if (alphaLevel < 0xfe) JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) alphaLevel)) else JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width))) } else { jassert (x >= 0 && x + width <= srcData.width); if (alphaLevel < 0xfe) JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) alphaLevel)) else copyRow (dest, getSrcPixel (x), width); } } void handleEdgeTableLineFull (int x, int width) const noexcept { DestPixelType* dest = getDestPixel (x); x -= xOffset; if (repeatPattern) { if (extraAlpha < 0xfe) JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) extraAlpha)) else JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width))) } else { jassert (x >= 0 && x + width <= srcData.width); if (extraAlpha < 0xfe) JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) extraAlpha)) else copyRow (dest, getSrcPixel (x), width); } } void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) { jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width); SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset); uint8* mask = (uint8*) (s + x - xOffset); if (sizeof (SrcPixelType) == sizeof (PixelARGB)) mask += PixelARGB::indexA; et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width); } private: const Image::BitmapData& destData; const Image::BitmapData& srcData; const int extraAlpha, xOffset, yOffset; DestPixelType* linePixels; SrcPixelType* sourceLineStart; forcedinline DestPixelType* getDestPixel (int const x) const noexcept { return addBytesToPointer (linePixels, x * destData.pixelStride); } forcedinline SrcPixelType const* getSrcPixel (int const x) const noexcept { return addBytesToPointer (sourceLineStart, x * srcData.pixelStride); } forcedinline void copyRow (DestPixelType* dest, SrcPixelType const* src, int width) const noexcept { const int destStride = destData.pixelStride; const int srcStride = srcData.pixelStride; if (destStride == srcStride && srcData.pixelFormat == Image::RGB && destData.pixelFormat == Image::RGB) { memcpy (dest, src, (size_t) (width * srcStride)); } else { do { dest->blend (*src); dest = addBytesToPointer (dest, destStride); src = addBytesToPointer (src, srcStride); } while (--width > 0); } } JUCE_DECLARE_NON_COPYABLE (ImageFill) }; //============================================================================== /** Fills an edge-table with a transformed image. */ template class TransformedImageFill { public: TransformedImageFill (const Image::BitmapData& dest, const Image::BitmapData& src, const AffineTransform& transform, const int alpha, const Graphics::ResamplingQuality q) : interpolator (transform, q != Graphics::lowResamplingQuality ? 0.5f : 0.0f, q != Graphics::lowResamplingQuality ? -128 : 0), destData (dest), srcData (src), extraAlpha (alpha + 1), quality (q), maxX (src.width - 1), maxY (src.height - 1), scratchSize (2048) { scratchBuffer.malloc (scratchSize); } forcedinline void setEdgeTableYPos (const int newY) noexcept { y = newY; linePixels = (DestPixelType*) destData.getLinePointer (newY); } forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept { SrcPixelType p; generate (&p, x, 1); getDestPixel (x)->blend (p, (uint32) (alphaLevel * extraAlpha) >> 8); } forcedinline void handleEdgeTablePixelFull (const int x) noexcept { SrcPixelType p; generate (&p, x, 1); getDestPixel (x)->blend (p, (uint32) extraAlpha); } void handleEdgeTableLine (const int x, int width, int alphaLevel) noexcept { if (width > (int) scratchSize) { scratchSize = (size_t) width; scratchBuffer.malloc (scratchSize); } SrcPixelType* span = scratchBuffer; generate (span, x, width); DestPixelType* dest = getDestPixel (x); alphaLevel *= extraAlpha; alphaLevel >>= 8; if (alphaLevel < 0xfe) JUCE_PERFORM_PIXEL_OP_LOOP (blend (*span++, (uint32) alphaLevel)) else JUCE_PERFORM_PIXEL_OP_LOOP (blend (*span++)) } forcedinline void handleEdgeTableLineFull (const int x, int width) noexcept { handleEdgeTableLine (x, width, 255); } void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) { if (width > (int) scratchSize) { scratchSize = (size_t) width; scratchBuffer.malloc (scratchSize); } y = y_; generate (scratchBuffer.getData(), x, width); et.clipLineToMask (x, y_, reinterpret_cast (scratchBuffer.getData()) + SrcPixelType::indexA, sizeof (SrcPixelType), width); } private: forcedinline DestPixelType* getDestPixel (const int x) const noexcept { return addBytesToPointer (linePixels, x * destData.pixelStride); } //============================================================================== template void generate (PixelType* dest, const int x, int numPixels) noexcept { this->interpolator.setStartOfLine ((float) x, (float) y, numPixels); do { int hiResX, hiResY; this->interpolator.next (hiResX, hiResY); int loResX = hiResX >> 8; int loResY = hiResY >> 8; if (repeatPattern) { loResX = negativeAwareModulo (loResX, srcData.width); loResY = negativeAwareModulo (loResY, srcData.height); } if (quality != Graphics::lowResamplingQuality) { if (isPositiveAndBelow (loResX, maxX)) { if (isPositiveAndBelow (loResY, maxY)) { // In the centre of the image.. render4PixelAverage (dest, this->srcData.getPixelPointer (loResX, loResY), hiResX & 255, hiResY & 255); ++dest; continue; } if (! repeatPattern) { // At a top or bottom edge.. if (loResY < 0) render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, 0), hiResX & 255); else render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, maxY), hiResX & 255); ++dest; continue; } } else { if (isPositiveAndBelow (loResY, maxY) && ! repeatPattern) { // At a left or right hand edge.. if (loResX < 0) render2PixelAverageY (dest, this->srcData.getPixelPointer (0, loResY), hiResY & 255); else render2PixelAverageY (dest, this->srcData.getPixelPointer (maxX, loResY), hiResY & 255); ++dest; continue; } } } if (! repeatPattern) { if (loResX < 0) loResX = 0; if (loResY < 0) loResY = 0; if (loResX > maxX) loResX = maxX; if (loResY > maxY) loResY = maxY; } dest->set (*(const PixelType*) this->srcData.getPixelPointer (loResX, loResY)); ++dest; } while (--numPixels > 0); } //============================================================================== void render4PixelAverage (PixelARGB* const dest, const uint8* src, const int subPixelX, const int subPixelY) noexcept { uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 }; uint32 weight = (uint32) ((256 - subPixelX) * (256 - subPixelY)); c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; src += this->srcData.pixelStride; weight = (uint32) (subPixelX * (256 - subPixelY)); c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; src += this->srcData.lineStride; weight = (uint32) (subPixelX * subPixelY); c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; src -= this->srcData.pixelStride; weight = (uint32) ((256 - subPixelX) * subPixelY); c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16), (uint8) (c[PixelARGB::indexR] >> 16), (uint8) (c[PixelARGB::indexG] >> 16), (uint8) (c[PixelARGB::indexB] >> 16)); } void render2PixelAverageX (PixelARGB* const dest, const uint8* src, const uint32 subPixelX) noexcept { uint32 c[4] = { 128, 128, 128, 128 }; uint32 weight = 256 - subPixelX; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; src += this->srcData.pixelStride; weight = subPixelX; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8), (uint8) (c[PixelARGB::indexR] >> 8), (uint8) (c[PixelARGB::indexG] >> 8), (uint8) (c[PixelARGB::indexB] >> 8)); } void render2PixelAverageY (PixelARGB* const dest, const uint8* src, const uint32 subPixelY) noexcept { uint32 c[4] = { 128, 128, 128, 128 }; uint32 weight = 256 - subPixelY; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; src += this->srcData.lineStride; weight = subPixelY; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; c[3] += weight * src[3]; dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8), (uint8) (c[PixelARGB::indexR] >> 8), (uint8) (c[PixelARGB::indexG] >> 8), (uint8) (c[PixelARGB::indexB] >> 8)); } //============================================================================== void render4PixelAverage (PixelRGB* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept { uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 }; uint32 weight = (256 - subPixelX) * (256 - subPixelY); c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; src += this->srcData.pixelStride; weight = subPixelX * (256 - subPixelY); c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; src += this->srcData.lineStride; weight = subPixelX * subPixelY; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; src -= this->srcData.pixelStride; weight = (256 - subPixelX) * subPixelY; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; dest->setARGB ((uint8) 255, (uint8) (c[PixelRGB::indexR] >> 16), (uint8) (c[PixelRGB::indexG] >> 16), (uint8) (c[PixelRGB::indexB] >> 16)); } void render2PixelAverageX (PixelRGB* const dest, const uint8* src, const uint32 subPixelX) noexcept { uint32 c[3] = { 128, 128, 128 }; const uint32 weight = 256 - subPixelX; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; src += this->srcData.pixelStride; c[0] += subPixelX * src[0]; c[1] += subPixelX * src[1]; c[2] += subPixelX * src[2]; dest->setARGB ((uint8) 255, (uint8) (c[PixelRGB::indexR] >> 8), (uint8) (c[PixelRGB::indexG] >> 8), (uint8) (c[PixelRGB::indexB] >> 8)); } void render2PixelAverageY (PixelRGB* const dest, const uint8* src, const uint32 subPixelY) noexcept { uint32 c[3] = { 128, 128, 128 }; const uint32 weight = 256 - subPixelY; c[0] += weight * src[0]; c[1] += weight * src[1]; c[2] += weight * src[2]; src += this->srcData.lineStride; c[0] += subPixelY * src[0]; c[1] += subPixelY * src[1]; c[2] += subPixelY * src[2]; dest->setARGB ((uint8) 255, (uint8) (c[PixelRGB::indexR] >> 8), (uint8) (c[PixelRGB::indexG] >> 8), (uint8) (c[PixelRGB::indexB] >> 8)); } //============================================================================== void render4PixelAverage (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept { uint32 c = 256 * 128; c += src[0] * ((256 - subPixelX) * (256 - subPixelY)); src += this->srcData.pixelStride; c += src[1] * (subPixelX * (256 - subPixelY)); src += this->srcData.lineStride; c += src[1] * (subPixelX * subPixelY); src -= this->srcData.pixelStride; c += src[0] * ((256 - subPixelX) * subPixelY); *((uint8*) dest) = (uint8) (c >> 16); } void render2PixelAverageX (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX) noexcept { uint32 c = 128; c += src[0] * (256 - subPixelX); src += this->srcData.pixelStride; c += src[0] * subPixelX; *((uint8*) dest) = (uint8) (c >> 8); } void render2PixelAverageY (PixelAlpha* const dest, const uint8* src, const uint32 subPixelY) noexcept { uint32 c = 128; c += src[0] * (256 - subPixelY); src += this->srcData.lineStride; c += src[0] * subPixelY; *((uint8*) dest) = (uint8) (c >> 8); } //============================================================================== class TransformedImageSpanInterpolator { public: TransformedImageSpanInterpolator (const AffineTransform& transform, const float offsetFloat, const int offsetInt) noexcept : inverseTransform (transform.inverted()), pixelOffset (offsetFloat), pixelOffsetInt (offsetInt) {} void setStartOfLine (float sx, float sy, const int numPixels) noexcept { jassert (numPixels > 0); sx += pixelOffset; sy += pixelOffset; float x1 = sx, y1 = sy; sx += (float) numPixels; inverseTransform.transformPoints (x1, y1, sx, sy); xBresenham.set ((int) (x1 * 256.0f), (int) (sx * 256.0f), numPixels, pixelOffsetInt); yBresenham.set ((int) (y1 * 256.0f), (int) (sy * 256.0f), numPixels, pixelOffsetInt); } void next (int& px, int& py) noexcept { px = xBresenham.n; xBresenham.stepToNext(); py = yBresenham.n; yBresenham.stepToNext(); } private: class BresenhamInterpolator { public: BresenhamInterpolator() noexcept {} void set (const int n1, const int n2, const int steps, const int offsetInt) noexcept { numSteps = steps; step = (n2 - n1) / numSteps; remainder = modulo = (n2 - n1) % numSteps; n = n1 + offsetInt; if (modulo <= 0) { modulo += numSteps; remainder += numSteps; --step; } modulo -= numSteps; } forcedinline void stepToNext() noexcept { modulo += remainder; n += step; if (modulo > 0) { modulo -= numSteps; ++n; } } int n; private: int numSteps, step, modulo, remainder; }; const AffineTransform inverseTransform; BresenhamInterpolator xBresenham, yBresenham; const float pixelOffset; const int pixelOffsetInt; JUCE_DECLARE_NON_COPYABLE (TransformedImageSpanInterpolator) }; //============================================================================== TransformedImageSpanInterpolator interpolator; const Image::BitmapData& destData; const Image::BitmapData& srcData; const int extraAlpha; const Graphics::ResamplingQuality quality; const int maxX, maxY; int y; DestPixelType* linePixels; HeapBlock scratchBuffer; size_t scratchSize; JUCE_DECLARE_NON_COPYABLE (TransformedImageFill) }; //============================================================================== template void renderImageTransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) { switch (destData.pixelFormat) { case Image::ARGB: switch (srcData.pixelFormat) { case Image::ARGB: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; case Image::RGB: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; default: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; } break; case Image::RGB: switch (srcData.pixelFormat) { case Image::ARGB: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; case Image::RGB: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; default: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; } break; default: switch (srcData.pixelFormat) { case Image::ARGB: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; case Image::RGB: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; default: if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } break; } break; } } template void renderImageUntransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) { switch (destData.pixelFormat) { case Image::ARGB: switch (srcData.pixelFormat) { case Image::ARGB: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; case Image::RGB: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; default: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; } break; case Image::RGB: switch (srcData.pixelFormat) { case Image::ARGB: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; case Image::RGB: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; default: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; } break; default: switch (srcData.pixelFormat) { case Image::ARGB: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; case Image::RGB: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; default: if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } break; } break; } } template void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, const PixelARGB fillColour, const bool replaceContents, DestPixelType*) { if (replaceContents) { EdgeTableFillers::SolidColour r (destData, fillColour); iter.iterate (r); } else { EdgeTableFillers::SolidColour r (destData, fillColour); iter.iterate (r); } } template void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform, const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) { if (g.isRadial) { if (isIdentity) { EdgeTableFillers::Gradient renderer (destData, g, transform, lookupTable, numLookupEntries); iter.iterate (renderer); } else { EdgeTableFillers::Gradient renderer (destData, g, transform, lookupTable, numLookupEntries); iter.iterate (renderer); } } else { EdgeTableFillers::Gradient renderer (destData, g, transform, lookupTable, numLookupEntries); iter.iterate (renderer); } } } //============================================================================== template struct ClipRegions { class Base : public SingleThreadedReferenceCountedObject { public: Base() {} virtual ~Base() {} typedef ReferenceCountedObjectPtr Ptr; virtual Ptr clone() const = 0; virtual Ptr applyClipTo (const Ptr& target) const = 0; virtual Ptr clipToRectangle (const Rectangle&) = 0; virtual Ptr clipToRectangleList (const RectangleList&) = 0; virtual Ptr excludeClipRectangle (const Rectangle&) = 0; virtual Ptr clipToPath (const Path&, const AffineTransform&) = 0; virtual Ptr clipToEdgeTable (const EdgeTable& et) = 0; virtual Ptr clipToImageAlpha (const Image&, const AffineTransform&, const Graphics::ResamplingQuality) = 0; virtual void translate (Point delta) = 0; virtual bool clipRegionIntersects (const Rectangle&) const = 0; virtual Rectangle getClipBounds() const = 0; virtual void fillRectWithColour (SavedStateType&, const Rectangle&, const PixelARGB colour, bool replaceContents) const = 0; virtual void fillRectWithColour (SavedStateType&, const Rectangle&, const PixelARGB colour) const = 0; virtual void fillAllWithColour (SavedStateType&, const PixelARGB colour, bool replaceContents) const = 0; virtual void fillAllWithGradient (SavedStateType&, ColourGradient&, const AffineTransform&, bool isIdentity) const = 0; virtual void renderImageTransformed (SavedStateType&, const Image&, const int alpha, const AffineTransform&, Graphics::ResamplingQuality, bool tiledFill) const = 0; virtual void renderImageUntransformed (SavedStateType&, const Image&, const int alpha, int x, int y, bool tiledFill) const = 0; }; //============================================================================== class EdgeTableRegion : public Base { public: EdgeTableRegion (const EdgeTable& e) : edgeTable (e) {} EdgeTableRegion (const Rectangle& r) : edgeTable (r) {} EdgeTableRegion (const Rectangle& r) : edgeTable (r) {} EdgeTableRegion (const RectangleList& r) : edgeTable (r) {} EdgeTableRegion (const RectangleList& r) : edgeTable (r) {} EdgeTableRegion (const Rectangle& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {} EdgeTableRegion (const EdgeTableRegion& other) : Base(), edgeTable (other.edgeTable) {} typedef typename Base::Ptr Ptr; Ptr clone() const { return new EdgeTableRegion (*this); } Ptr applyClipTo (const Ptr& target) const { return target->clipToEdgeTable (edgeTable); } Ptr clipToRectangle (const Rectangle& r) { edgeTable.clipToRectangle (r); return edgeTable.isEmpty() ? nullptr : this; } Ptr clipToRectangleList (const RectangleList& r) { RectangleList inverse (edgeTable.getMaximumBounds()); if (inverse.subtract (r)) for (const Rectangle* i = inverse.begin(), * const e = inverse.end(); i != e; ++i) edgeTable.excludeRectangle (*i); return edgeTable.isEmpty() ? nullptr : this; } Ptr excludeClipRectangle (const Rectangle& r) { edgeTable.excludeRectangle (r); return edgeTable.isEmpty() ? nullptr : this; } Ptr clipToPath (const Path& p, const AffineTransform& transform) { EdgeTable et (edgeTable.getMaximumBounds(), p, transform); edgeTable.clipToEdgeTable (et); return edgeTable.isEmpty() ? nullptr : this; } Ptr clipToEdgeTable (const EdgeTable& et) { edgeTable.clipToEdgeTable (et); return edgeTable.isEmpty() ? nullptr : this; } Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) { const Image::BitmapData srcData (image, Image::BitmapData::readOnly); if (transform.isOnlyTranslation()) { // If our translation doesn't involve any distortion, just use a simple blit.. const int tx = (int) (transform.getTranslationX() * 256.0f); const int ty = (int) (transform.getTranslationY() * 256.0f); if (quality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0) { const int imageX = ((tx + 128) >> 8); const int imageY = ((ty + 128) >> 8); if (image.getFormat() == Image::ARGB) straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0); else straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0); return edgeTable.isEmpty() ? nullptr : this; } } if (transform.isSingularity()) return Ptr(); { Path p; p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform); edgeTable.clipToEdgeTable (et2); } if (! edgeTable.isEmpty()) { if (image.getFormat() == Image::ARGB) transformedClipImage (srcData, transform, quality, (PixelARGB*) 0); else transformedClipImage (srcData, transform, quality, (PixelAlpha*) 0); } return edgeTable.isEmpty() ? nullptr : this; } void translate (Point delta) { edgeTable.translate ((float) delta.x, delta.y); } bool clipRegionIntersects (const Rectangle& r) const { return edgeTable.getMaximumBounds().intersects (r); } Rectangle getClipBounds() const { return edgeTable.getMaximumBounds(); } void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour, bool replaceContents) const { const Rectangle totalClip (edgeTable.getMaximumBounds()); const Rectangle clipped (totalClip.getIntersection (area)); if (! clipped.isEmpty()) { EdgeTableRegion et (clipped); et.edgeTable.clipToEdgeTable (edgeTable); state.fillWithSolidColour (et.edgeTable, colour, replaceContents); } } void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour) const { const Rectangle totalClip (edgeTable.getMaximumBounds().toFloat()); const Rectangle clipped (totalClip.getIntersection (area)); if (! clipped.isEmpty()) { EdgeTableRegion et (clipped); et.edgeTable.clipToEdgeTable (edgeTable); state.fillWithSolidColour (et.edgeTable, colour, false); } } void fillAllWithColour (SavedStateType& state, const PixelARGB colour, bool replaceContents) const { state.fillWithSolidColour (edgeTable, colour, replaceContents); } void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const { state.fillWithGradient (edgeTable, gradient, transform, isIdentity); } void renderImageTransformed (SavedStateType& state, const Image& src, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const { state.renderImageTransformed (edgeTable, src, alpha, transform, quality, tiledFill); } void renderImageUntransformed (SavedStateType& state, const Image& src, const int alpha, int x, int y, bool tiledFill) const { state.renderImageUntransformed (edgeTable, src, alpha, x, y, tiledFill); } EdgeTable edgeTable; private: template void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const Graphics::ResamplingQuality quality, const SrcPixelType*) { EdgeTableFillers::TransformedImageFill renderer (srcData, srcData, transform, 255, quality); for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y) renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(), edgeTable.getMaximumBounds().getWidth()); } template void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*) { Rectangle r (imageX, imageY, srcData.width, srcData.height); edgeTable.clipToRectangle (r); EdgeTableFillers::ImageFill renderer (srcData, srcData, 255, imageX, imageY); for (int y = 0; y < r.getHeight(); ++y) renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth()); } EdgeTableRegion& operator= (const EdgeTableRegion&); }; //============================================================================== class RectangleListRegion : public Base { public: RectangleListRegion (const Rectangle& r) : clip (r) {} RectangleListRegion (const RectangleList& r) : clip (r) {} RectangleListRegion (const RectangleListRegion& other) : Base(), clip (other.clip) {} typedef typename Base::Ptr Ptr; Ptr clone() const { return new RectangleListRegion (*this); } Ptr applyClipTo (const Ptr& target) const { return target->clipToRectangleList (clip); } Ptr clipToRectangle (const Rectangle& r) { clip.clipTo (r); return clip.isEmpty() ? nullptr : this; } Ptr clipToRectangleList (const RectangleList& r) { clip.clipTo (r); return clip.isEmpty() ? nullptr : this; } Ptr excludeClipRectangle (const Rectangle& r) { clip.subtract (r); return clip.isEmpty() ? nullptr : this; } Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toEdgeTable()->clipToPath (p, transform); } Ptr clipToEdgeTable (const EdgeTable& et) { return toEdgeTable()->clipToEdgeTable (et); } Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) { return toEdgeTable()->clipToImageAlpha (image, transform, quality); } void translate (Point delta) { clip.offsetAll (delta); } bool clipRegionIntersects (const Rectangle& r) const { return clip.intersects (r); } Rectangle getClipBounds() const { return clip.getBounds(); } void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour, bool replaceContents) const { SubRectangleIterator iter (clip, area); state.fillWithSolidColour (iter, colour, replaceContents); } void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour) const { SubRectangleIteratorFloat iter (clip, area); state.fillWithSolidColour (iter, colour, false); } void fillAllWithColour (SavedStateType& state, const PixelARGB colour, bool replaceContents) const { state.fillWithSolidColour (*this, colour, replaceContents); } void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const { state.fillWithGradient (*this, gradient, transform, isIdentity); } void renderImageTransformed (SavedStateType& state, const Image& src, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const { state.renderImageTransformed (*this, src, alpha, transform, quality, tiledFill); } void renderImageUntransformed (SavedStateType& state, const Image& src, const int alpha, int x, int y, bool tiledFill) const { state.renderImageUntransformed (*this, src, alpha, x, y, tiledFill); } RectangleList clip; //============================================================================== template void iterate (Renderer& r) const noexcept { for (const Rectangle* i = clip.begin(), * const e = clip.end(); i != e; ++i) { const int x = i->getX(); const int w = i->getWidth(); jassert (w > 0); const int bottom = i->getBottom(); for (int y = i->getY(); y < bottom; ++y) { r.setEdgeTableYPos (y); r.handleEdgeTableLineFull (x, w); } } } private: //============================================================================== class SubRectangleIterator { public: SubRectangleIterator (const RectangleList& clipList, const Rectangle& clipBounds) : clip (clipList), area (clipBounds) {} template void iterate (Renderer& r) const noexcept { for (const Rectangle* i = clip.begin(), * const e = clip.end(); i != e; ++i) { const Rectangle rect (i->getIntersection (area)); if (! rect.isEmpty()) { const int x = rect.getX(); const int w = rect.getWidth(); const int bottom = rect.getBottom(); for (int y = rect.getY(); y < bottom; ++y) { r.setEdgeTableYPos (y); r.handleEdgeTableLineFull (x, w); } } } } private: const RectangleList& clip; const Rectangle area; JUCE_DECLARE_NON_COPYABLE (SubRectangleIterator) }; //============================================================================== class SubRectangleIteratorFloat { public: SubRectangleIteratorFloat (const RectangleList& clipList, const Rectangle& clipBounds) noexcept : clip (clipList), area (clipBounds) { } template void iterate (Renderer& r) const noexcept { const RenderingHelpers::FloatRectangleRasterisingInfo f (area); for (const Rectangle* i = clip.begin(), * const e = clip.end(); i != e; ++i) { const int clipLeft = i->getX(); const int clipRight = i->getRight(); const int clipTop = i->getY(); const int clipBottom = i->getBottom(); if (f.totalBottom > clipTop && f.totalTop < clipBottom && f.totalRight > clipLeft && f.totalLeft < clipRight) { if (f.isOnePixelWide()) { if (f.topAlpha != 0 && f.totalTop >= clipTop) { r.setEdgeTableYPos (f.totalTop); r.handleEdgeTablePixel (f.left, f.topAlpha); } const int endY = jmin (f.bottom, clipBottom); for (int y = jmax (clipTop, f.top); y < endY; ++y) { r.setEdgeTableYPos (y); r.handleEdgeTablePixelFull (f.left); } if (f.bottomAlpha != 0 && f.bottom < clipBottom) { r.setEdgeTableYPos (f.bottom); r.handleEdgeTablePixel (f.left, f.bottomAlpha); } } else { const int clippedLeft = jmax (f.left, clipLeft); const int clippedWidth = jmin (f.right, clipRight) - clippedLeft; const bool doLeftAlpha = f.leftAlpha != 0 && f.totalLeft >= clipLeft; const bool doRightAlpha = f.rightAlpha != 0 && f.right < clipRight; if (f.topAlpha != 0 && f.totalTop >= clipTop) { r.setEdgeTableYPos (f.totalTop); if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getTopLeftCornerAlpha()); if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.topAlpha); if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getTopRightCornerAlpha()); } const int endY = jmin (f.bottom, clipBottom); for (int y = jmax (clipTop, f.top); y < endY; ++y) { r.setEdgeTableYPos (y); if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.leftAlpha); if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth); if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.rightAlpha); } if (f.bottomAlpha != 0 && f.bottom < clipBottom) { r.setEdgeTableYPos (f.bottom); if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getBottomLeftCornerAlpha()); if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.bottomAlpha); if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getBottomRightCornerAlpha()); } } } } } private: const RectangleList& clip; const Rectangle& area; JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat) }; Ptr toEdgeTable() const { return new EdgeTableRegion (clip); } RectangleListRegion& operator= (const RectangleListRegion&); }; }; //============================================================================== template class SavedStateBase { public: typedef typename ClipRegions::Base BaseRegionType; typedef typename ClipRegions::EdgeTableRegion EdgeTableRegionType; typedef typename ClipRegions::RectangleListRegion RectangleListRegionType; SavedStateBase (const Rectangle& initialClip) : clip (new RectangleListRegionType (initialClip)), transform (Point()), interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f) { } SavedStateBase (const RectangleList& clipList, Point origin) : clip (new RectangleListRegionType (clipList)), transform (origin), interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f) { } SavedStateBase (const SavedStateBase& other) : clip (other.clip), transform (other.transform), fillType (other.fillType), interpolationQuality (other.interpolationQuality), transparencyLayerAlpha (other.transparencyLayerAlpha) { } SavedStateType& getThis() noexcept { return *static_cast (this); } bool clipToRectangle (const Rectangle& r) { if (clip != nullptr) { if (transform.isOnlyTranslated) { cloneClipIfMultiplyReferenced(); clip = clip->clipToRectangle (transform.translated (r)); } else if (! transform.isRotated) { cloneClipIfMultiplyReferenced(); clip = clip->clipToRectangle (transform.transformed (r)); } else { Path p; p.addRectangle (r); clipToPath (p, AffineTransform::identity); } } return clip != nullptr; } bool clipToRectangleList (const RectangleList& r) { if (clip != nullptr) { if (transform.isOnlyTranslated) { cloneClipIfMultiplyReferenced(); RectangleList offsetList (r); offsetList.offsetAll (transform.offset.x, transform.offset.y); clip = clip->clipToRectangleList (offsetList); } else if (! transform.isRotated) { cloneClipIfMultiplyReferenced(); RectangleList scaledList; for (const Rectangle* i = r.begin(), * const e = r.end(); i != e; ++i) scaledList.add (transform.transformed (*i)); clip = clip->clipToRectangleList (scaledList); } else { clipToPath (r.toPath(), AffineTransform::identity); } } return clip != nullptr; } static Rectangle getLargestIntegerWithin (Rectangle r) { const int x1 = (int) std::ceil (r.getX()); const int y1 = (int) std::ceil (r.getY()); const int x2 = (int) std::floor (r.getRight()); const int y2 = (int) std::floor (r.getBottom()); return Rectangle (x1, y1, x2 - x1, y2 - y1); } bool excludeClipRectangle (const Rectangle& r) { if (clip != nullptr) { cloneClipIfMultiplyReferenced(); if (transform.isOnlyTranslated) { clip = clip->excludeClipRectangle (getLargestIntegerWithin (transform.translated (r.toFloat()))); } else if (! transform.isRotated) { clip = clip->excludeClipRectangle (getLargestIntegerWithin (transform.transformed (r.toFloat()))); } else { Path p; p.addRectangle (r.toFloat()); p.applyTransform (transform.complexTransform); p.addRectangle (clip->getClipBounds().toFloat()); p.setUsingNonZeroWinding (false); clip = clip->clipToPath (p, AffineTransform::identity); } } return clip != nullptr; } void clipToPath (const Path& p, const AffineTransform& t) { if (clip != nullptr) { cloneClipIfMultiplyReferenced(); clip = clip->clipToPath (p, transform.getTransformWith (t)); } } void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t) { if (clip != nullptr) { if (sourceImage.hasAlphaChannel()) { cloneClipIfMultiplyReferenced(); clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t), interpolationQuality); } else { Path p; p.addRectangle (sourceImage.getBounds()); clipToPath (p, t); } } } bool clipRegionIntersects (const Rectangle& r) const { if (clip != nullptr) { if (transform.isOnlyTranslated) return clip->clipRegionIntersects (transform.translated (r)); return getClipBounds().intersects (r); } return false; } Rectangle getClipBounds() const { return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds()) : Rectangle(); } void setFillType (const FillType& newFill) { fillType = newFill; } void fillTargetRect (const Rectangle& r, const bool replaceContents) { if (fillType.isColour()) { clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB(), replaceContents); } else { const Rectangle clipped (clip->getClipBounds().getIntersection (r)); if (! clipped.isEmpty()) fillShape (new RectangleListRegionType (clipped), false); } } void fillTargetRect (const Rectangle& r) { if (fillType.isColour()) { clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB()); } else { const Rectangle clipped (clip->getClipBounds().toFloat().getIntersection (r)); if (! clipped.isEmpty()) fillShape (new EdgeTableRegionType (clipped), false); } } template void fillRectAsPath (const Rectangle& r) { Path p; p.addRectangle (r); fillPath (p, AffineTransform::identity); } void fillRect (const Rectangle& r, const bool replaceContents) { if (clip != nullptr) { if (transform.isOnlyTranslated) { fillTargetRect (transform.translated (r), replaceContents); } else if (! transform.isRotated) { fillTargetRect (transform.transformed (r), replaceContents); } else { jassert (! replaceContents); // not implemented.. fillRectAsPath (r); } } } void fillRect (const Rectangle& r) { if (clip != nullptr) { if (transform.isOnlyTranslated) fillTargetRect (transform.translated (r)); else if (! transform.isRotated) fillTargetRect (transform.transformed (r)); else fillRectAsPath (r); } } void fillRectList (const RectangleList& list) { if (clip != nullptr) { if (! transform.isRotated) { RectangleList transformed (list); if (transform.isOnlyTranslated) transformed.offsetAll (transform.offset.toFloat()); else transformed.transformAll (transform.getTransform()); fillShape (new EdgeTableRegionType (transformed), false); } else { fillPath (list.toPath(), AffineTransform::identity); } } } void fillPath (const Path& path, const AffineTransform& t) { if (clip != nullptr) fillShape (new EdgeTableRegionType (clip->getClipBounds(), path, transform.getTransformWith (t)), false); } void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y) { if (clip != nullptr) { EdgeTableRegionType* edgeTableClip = new EdgeTableRegionType (edgeTable); edgeTableClip->edgeTable.translate (x, y); if (fillType.isColour()) { float brightness = fillType.colour.getBrightness() - 0.5f; if (brightness > 0.0f) edgeTableClip->edgeTable.multiplyLevels (1.0f + 1.6f * brightness); } fillShape (edgeTableClip, false); } } void drawLine (const Line& line) { Path p; p.addLineSegment (line, 1.0f); fillPath (p, AffineTransform::identity); } void drawImage (const Image& sourceImage, const AffineTransform& trans) { if (clip != nullptr && ! fillType.colour.isTransparent()) renderImage (sourceImage, trans, nullptr); } static bool isOnlyTranslationAllowingError (const AffineTransform& t) { return (std::abs (t.mat01) < 0.002) && (std::abs (t.mat10) < 0.002) && (std::abs (t.mat00 - 1.0f) < 0.002) && (std::abs (t.mat11 - 1.0f) < 0.002); } void renderImage (const Image& sourceImage, const AffineTransform& trans, const BaseRegionType* const tiledFillClipRegion) { const AffineTransform t (transform.getTransformWith (trans)); const int alpha = fillType.colour.getAlpha(); if (isOnlyTranslationAllowingError (t)) { // If our translation doesn't involve any distortion, just use a simple blit.. int tx = (int) (t.getTranslationX() * 256.0f); int ty = (int) (t.getTranslationY() * 256.0f); if (interpolationQuality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0) { tx = ((tx + 128) >> 8); ty = ((ty + 128) >> 8); if (tiledFillClipRegion != nullptr) { tiledFillClipRegion->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, true); } else { Rectangle area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()); area = area.getIntersection (getThis().getMaximumBounds()); if (! area.isEmpty()) if (typename BaseRegionType::Ptr c = clip->applyClipTo (new EdgeTableRegionType (area))) c->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, false); } return; } } if (! t.isSingularity()) { if (tiledFillClipRegion != nullptr) { tiledFillClipRegion->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, true); } else { Path p; p.addRectangle (sourceImage.getBounds()); typename BaseRegionType::Ptr c (clip->clone()); c = c->clipToPath (p, t); if (c != nullptr) c->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, false); } } } void fillShape (typename BaseRegionType::Ptr shapeToFill, const bool replaceContents) { jassert (clip != nullptr); shapeToFill = clip->applyClipTo (shapeToFill); if (shapeToFill != nullptr) { if (fillType.isGradient()) { jassert (! replaceContents); // that option is just for solid colours ColourGradient g2 (*(fillType.gradient)); g2.multiplyOpacity (fillType.getOpacity()); AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f)); const bool isIdentity = t.isOnlyTranslation(); if (isIdentity) { // If our translation doesn't involve any distortion, we can speed it up.. g2.point1.applyTransform (t); g2.point2.applyTransform (t); t = AffineTransform::identity; } shapeToFill->fillAllWithGradient (getThis(), g2, t, isIdentity); } else if (fillType.isTiledImage()) { renderImage (fillType.image, fillType.transform, shapeToFill); } else { shapeToFill->fillAllWithColour (getThis(), fillType.colour.getPixelARGB(), replaceContents); } } } void cloneClipIfMultiplyReferenced() { if (clip->getReferenceCount() > 1) clip = clip->clone(); } typename BaseRegionType::Ptr clip; RenderingHelpers::TranslationOrTransform transform; FillType fillType; Graphics::ResamplingQuality interpolationQuality; float transparencyLayerAlpha; }; //============================================================================== class SoftwareRendererSavedState : public SavedStateBase { typedef SavedStateBase BaseClass; public: SoftwareRendererSavedState (const Image& im, const Rectangle& clipBounds) : BaseClass (clipBounds), image (im) { } SoftwareRendererSavedState (const Image& im, const RectangleList& clipList, Point origin) : BaseClass (clipList, origin), image (im) { } SoftwareRendererSavedState (const SoftwareRendererSavedState& other) : BaseClass (other), image (other.image), font (other.font) { } SoftwareRendererSavedState* beginTransparencyLayer (float opacity) { SoftwareRendererSavedState* s = new SoftwareRendererSavedState (*this); if (clip != nullptr) { const Rectangle layerBounds (clip->getClipBounds()); s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true); s->transparencyLayerAlpha = opacity; s->transform.moveOriginInDeviceSpace (-layerBounds.getPosition()); s->cloneClipIfMultiplyReferenced(); s->clip->translate (-layerBounds.getPosition()); } return s; } void endTransparencyLayer (SoftwareRendererSavedState& finishedLayerState) { if (clip != nullptr) { const Rectangle layerBounds (clip->getClipBounds()); const ScopedPointer g (image.createLowLevelContext()); g->setOpacity (finishedLayerState.transparencyLayerAlpha); g->drawImage (finishedLayerState.image, AffineTransform::translation (layerBounds.getPosition())); } } typedef GlyphCache, SoftwareRendererSavedState> GlyphCacheType; static void clearGlyphCache() { GlyphCacheType::getInstance().reset(); } //============================================================================== void drawGlyph (int glyphNumber, const AffineTransform& trans) { if (clip != nullptr) { if (trans.isOnlyTranslation() && ! transform.isRotated) { GlyphCacheType& cache = GlyphCacheType::getInstance(); Point pos (trans.getTranslationX(), trans.getTranslationY()); if (transform.isOnlyTranslated) { cache.drawGlyph (*this, font, glyphNumber, pos + transform.offset.toFloat()); } else { pos = transform.transformed (pos); Font f (font); f.setHeight (font.getHeight() * transform.complexTransform.mat11); const float xScale = transform.complexTransform.mat00 / transform.complexTransform.mat11; if (std::abs (xScale - 1.0f) > 0.01f) f.setHorizontalScale (xScale); cache.drawGlyph (*this, f, glyphNumber, pos); } } else { const float fontHeight = font.getHeight(); AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) .followedBy (trans))); const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); if (et != nullptr) fillShape (new EdgeTableRegionType (*et), false); } } } Rectangle getMaximumBounds() const { return image.getBounds(); } //============================================================================== template void renderImageTransformed (IteratorType& iter, const Image& src, const int alpha, const AffineTransform& trans, Graphics::ResamplingQuality quality, bool tiledFill) const { Image::BitmapData destData (image, Image::BitmapData::readWrite); const Image::BitmapData srcData (src, Image::BitmapData::readOnly); EdgeTableFillers::renderImageTransformed (iter, destData, srcData, alpha, trans, quality, tiledFill); } template void renderImageUntransformed (IteratorType& iter, const Image& src, const int alpha, int x, int y, bool tiledFill) const { Image::BitmapData destData (image, Image::BitmapData::readWrite); const Image::BitmapData srcData (src, Image::BitmapData::readOnly); EdgeTableFillers::renderImageUntransformed (iter, destData, srcData, alpha, x, y, tiledFill); } template void fillWithSolidColour (IteratorType& iter, const PixelARGB colour, bool replaceContents) const { Image::BitmapData destData (image, Image::BitmapData::readWrite); switch (destData.pixelFormat) { case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break; case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break; default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break; } } template void fillWithGradient (IteratorType& iter, ColourGradient& gradient, const AffineTransform& trans, bool isIdentity) const { HeapBlock lookupTable; const int numLookupEntries = gradient.createLookupTable (trans, lookupTable); jassert (numLookupEntries > 0); Image::BitmapData destData (image, Image::BitmapData::readWrite); switch (destData.pixelFormat) { case Image::ARGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; case Image::RGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; default: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; } } //============================================================================== Image image; Font font; private: SoftwareRendererSavedState& operator= (const SoftwareRendererSavedState&); }; //============================================================================== template class SavedStateStack { public: SavedStateStack (StateObjectType* const initialState) noexcept : currentState (initialState) {} SavedStateStack() noexcept {} void initialise (StateObjectType* state) { currentState = state; } inline StateObjectType* operator->() const noexcept { return currentState; } inline StateObjectType& operator*() const noexcept { return *currentState; } void save() { stack.add (new StateObjectType (*currentState)); } void restore() { if (StateObjectType* const top = stack.getLast()) { currentState = top; stack.removeLast (1, false); } else { jassertfalse; // trying to pop with an empty stack! } } void beginTransparencyLayer (float opacity) { save(); currentState = currentState->beginTransparencyLayer (opacity); } void endTransparencyLayer() { const ScopedPointer finishedTransparencyLayer (currentState); restore(); currentState->endTransparencyLayer (*finishedTransparencyLayer); } private: ScopedPointer currentState; OwnedArray stack; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedStateStack) }; //============================================================================== template class StackBasedLowLevelGraphicsContext : public LowLevelGraphicsContext { public: bool isVectorDevice() const override { return false; } void setOrigin (Point o) override { stack->transform.setOrigin (o); } void addTransform (const AffineTransform& t) override { stack->transform.addTransform (t); } float getPhysicalPixelScaleFactor() override { return stack->transform.getPhysicalPixelScaleFactor(); } Rectangle getClipBounds() const override { return stack->getClipBounds(); } bool isClipEmpty() const override { return stack->clip == nullptr; } bool clipRegionIntersects (const Rectangle& r) override { return stack->clipRegionIntersects (r); } bool clipToRectangle (const Rectangle& r) override { return stack->clipToRectangle (r); } bool clipToRectangleList (const RectangleList& r) override { return stack->clipToRectangleList (r); } void excludeClipRectangle (const Rectangle& r) override { stack->excludeClipRectangle (r); } void clipToPath (const Path& path, const AffineTransform& t) override { stack->clipToPath (path, t); } void clipToImageAlpha (const Image& im, const AffineTransform& t) override { stack->clipToImageAlpha (im, t); } void saveState() override { stack.save(); } void restoreState() override { stack.restore(); } void beginTransparencyLayer (float opacity) override { stack.beginTransparencyLayer (opacity); } void endTransparencyLayer() override { stack.endTransparencyLayer(); } void setFill (const FillType& fillType) override { stack->setFillType (fillType); } void setOpacity (float newOpacity) override { stack->fillType.setOpacity (newOpacity); } void setInterpolationQuality (Graphics::ResamplingQuality quality) override { stack->interpolationQuality = quality; } void fillRect (const Rectangle& r, bool replace) override { stack->fillRect (r, replace); } void fillRect (const Rectangle& r) override { stack->fillRect (r); } void fillRectList (const RectangleList& list) override { stack->fillRectList (list); } void fillPath (const Path& path, const AffineTransform& t) override { stack->fillPath (path, t); } void drawImage (const Image& im, const AffineTransform& t) override { stack->drawImage (im, t); } void drawGlyph (int glyphNumber, const AffineTransform& t) override { stack->drawGlyph (glyphNumber, t); } void drawLine (const Line& line) override { stack->drawLine (line); } void setFont (const Font& newFont) override { stack->font = newFont; } const Font& getFont() override { return stack->font; } protected: StackBasedLowLevelGraphicsContext (SavedStateType* initialState) : stack (initialState) {} StackBasedLowLevelGraphicsContext() {} RenderingHelpers::SavedStateStack stack; }; } #if JUCE_MSVC #pragma warning (pop) #endif #endif // JUCE_RENDERINGHELPERS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp000066400000000000000000000277111320201440200322130ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct DefaultFontNames { DefaultFontNames() : defaultSans ("sans"), defaultSerif ("serif"), defaultFixed ("monospace"), defaultFallback ("sans") { } String getRealFontName (const String& faceName) const { if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans; if (faceName == Font::getDefaultSerifFontName()) return defaultSerif; if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed; return faceName; } String defaultSans, defaultSerif, defaultFixed, defaultFallback; }; Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; Font f (font); f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName())); return Typeface::createSystemTypefaceFor (f); } //============================================================================== #if JUCE_USE_FREETYPE StringArray FTTypefaceList::getDefaultFontDirectories() { return StringArray ("/system/fonts"); } Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return new FreeTypeTypeface (font); } void Typeface::scanFolderForFonts (const File& folder) { FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName())); } StringArray Font::findAllTypefaceNames() { return FTTypefaceList::getInstance()->findAllFamilyNames(); } StringArray Font::findAllTypefaceStyles (const String& family) { return FTTypefaceList::getInstance()->findAllTypefaceStyles (family); } bool TextLayout::createNativeLayout (const AttributedString&) { return false; } #else //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \ STATICMETHOD (createFromFile, "createFromFile", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface"); #undef JNI_CLASS_MEMBERS //============================================================================== StringArray Font::findAllTypefaceNames() { StringArray results; Array fonts; File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, "*.ttf"); for (int i = 0; i < fonts.size(); ++i) results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension() .upToLastOccurrenceOf ("-", false, false)); return results; } StringArray Font::findAllTypefaceStyles (const String& family) { StringArray results ("Regular"); Array fonts; File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, family + "-*.ttf"); for (int i = 0; i < fonts.size(); ++i) results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension() .fromLastOccurrenceOf ("-", false, false)); return results; } const float referenceFontSize = 256.0f; const float referenceFontToUnits = 1.0f / referenceFontSize; //============================================================================== class AndroidTypeface : public Typeface { public: AndroidTypeface (const Font& font) : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), ascent (0), descent (0), heightToPointsFactor (1.0f) { JNIEnv* const env = getEnv(); // First check whether there's an embedded asset with this font name: typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromAsset, javaString ("fonts/" + name).get())); if (typeface.get() == nullptr) { const bool isBold = style.contains ("Bold"); const bool isItalic = style.contains ("Italic"); File fontFile (getFontFile (name, style)); if (! fontFile.exists()) fontFile = findFontFile (name, isBold, isItalic); if (fontFile.exists()) typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, javaString (fontFile.getFullPathName()).get())); else typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, javaString (getName()).get(), (isBold ? 1 : 0) + (isItalic ? 2 : 0))); } initialise (env); } AndroidTypeface (const void* data, size_t size) : Typeface (String(), String()) { JNIEnv* const env = getEnv(); LocalRef bytes (env->NewByteArray (size)); env->SetByteArrayRegion (bytes, 0, size, (const jbyte*) data); typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromByteArray, bytes.get())); initialise (env); } void initialise (JNIEnv* const env) { rect = GlobalRef (env->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0)); paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality)); const LocalRef ignored (paint.callObjectMethod (Paint.setTypeface, typeface.get())); paint.callVoidMethod (Paint.setTextSize, referenceFontSize); const float fullAscent = std::abs (paint.callFloatMethod (Paint.ascent)); const float fullDescent = paint.callFloatMethod (Paint.descent); const float totalHeight = fullAscent + fullDescent; ascent = fullAscent / totalHeight; descent = fullDescent / totalHeight; heightToPointsFactor = referenceFontSize / totalHeight; } float getAscent() const override { return ascent; } float getDescent() const override { return descent; } float getHeightToPointsFactor() const override { return heightToPointsFactor; } float getStringWidth (const String& text) override { JNIEnv* env = getEnv(); const int numChars = text.length(); jfloatArray widths = env->NewFloatArray (numChars); const int numDone = paint.callIntMethod (Paint.getTextWidths, javaString (text).get(), widths); HeapBlock localWidths (numDone); env->GetFloatArrayRegion (widths, 0, numDone, localWidths); env->DeleteLocalRef (widths); float x = 0; for (int i = 0; i < numDone; ++i) x += localWidths[i]; return x * referenceFontToUnits; } void getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) override { JNIEnv* env = getEnv(); const int numChars = text.length(); jfloatArray widths = env->NewFloatArray (numChars); const int numDone = paint.callIntMethod (Paint.getTextWidths, javaString (text).get(), widths); HeapBlock localWidths (numDone); env->GetFloatArrayRegion (widths, 0, numDone, localWidths); env->DeleteLocalRef (widths); String::CharPointerType s (text.getCharPointer()); xOffsets.add (0); float x = 0; for (int i = 0; i < numDone; ++i) { glyphs.add ((int) s.getAndAdvance()); x += localWidths[i]; xOffsets.add (x * referenceFontToUnits); } } bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) override { return false; } EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t, float /*fontHeight*/) override { JNIEnv* env = getEnv(); jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t)); jintArray maskData = (jintArray) android.activity.callObjectMethod (JuceAppActivity.renderGlyph, (jchar) glyphNumber, paint.get(), matrix, rect.get()); env->DeleteLocalRef (matrix); const int left = env->GetIntField (rect.get(), RectClass.left); const int top = env->GetIntField (rect.get(), RectClass.top); const int right = env->GetIntField (rect.get(), RectClass.right); const int bottom = env->GetIntField (rect.get(), RectClass.bottom); const Rectangle bounds (left, top, right - left, bottom - top); EdgeTable* et = nullptr; if (! bounds.isEmpty()) { et = new EdgeTable (bounds); jint* const maskDataElements = env->GetIntArrayElements (maskData, 0); const jint* mask = maskDataElements; for (int y = top; y < bottom; ++y) { #if JUCE_LITTLE_ENDIAN const uint8* const lineBytes = ((const uint8*) mask) + 3; #else const uint8* const lineBytes = (const uint8*) mask; #endif et->clipLineToMask (left, y, lineBytes, 4, bounds.getWidth()); mask += bounds.getWidth(); } env->ReleaseIntArrayElements (maskData, maskDataElements, 0); } env->DeleteLocalRef (maskData); return et; } GlobalRef typeface, paint, rect; float ascent, descent, heightToPointsFactor; private: static File findFontFile (const String& family, const bool bold, const bool italic) { File file; if (bold || italic) { String suffix; if (bold) suffix = "Bold"; if (italic) suffix << "Italic"; file = getFontFile (family, suffix); if (file.exists()) return file; } file = getFontFile (family, "Regular"); if (! file.exists()) file = getFontFile (family, String()); return file; } static File getFontFile (const String& family, const String& style) { String path ("/system/fonts/" + family); if (style.isNotEmpty()) path << '-' << style; return File (path + ".ttf"); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface) }; //============================================================================== Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return new AndroidTypeface (font); } Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { return new AndroidTypeface (data, size); } void Typeface::scanFolderForFonts (const File&) { jassertfalse; // not available unless using FreeType } bool TextLayout::createNativeLayout (const AttributedString&) { return false; } #endif juce_android_GraphicsContext.cpp000066400000000000000000000042071320201440200341430ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ namespace GraphicsHelpers { jobject createPaint (Graphics::ResamplingQuality quality) { jint constructorFlags = 1 /*ANTI_ALIAS_FLAG*/ | 4 /*DITHER_FLAG*/ | 128 /*SUBPIXEL_TEXT_FLAG*/; if (quality > Graphics::lowResamplingQuality) constructorFlags |= 2; /*FILTER_BITMAP_FLAG*/ return getEnv()->NewObject (Paint, Paint.constructor, constructorFlags); } const jobject createMatrix (JNIEnv* env, const AffineTransform& t) { jobject m = env->NewObject (Matrix, Matrix.constructor); jfloat values[9] = { t.mat00, t.mat01, t.mat02, t.mat10, t.mat11, t.mat12, 0.0f, 0.0f, 1.0f }; jfloatArray javaArray = env->NewFloatArray (9); env->SetFloatArrayRegion (javaArray, 0, 9, values); env->CallVoidMethod (m, Matrix.setValues, javaArray); env->DeleteLocalRef (javaArray); return m; } } ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { return SoftwareImageType().create (format, width, height, clearImage); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp000066400000000000000000000357321320201440200324200ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ struct FTLibWrapper : public ReferenceCountedObject { FTLibWrapper() : library (0) { if (FT_Init_FreeType (&library) != 0) { library = 0; DBG ("Failed to initialize FreeType"); } } ~FTLibWrapper() { if (library != 0) FT_Done_FreeType (library); } FT_Library library; typedef ReferenceCountedObjectPtr Ptr; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTLibWrapper) }; //============================================================================== struct FTFaceWrapper : public ReferenceCountedObject { FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex) : face (0), library (ftLib) { if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0) face = 0; } FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const void* data, size_t dataSize, int faceIndex) : face (0), library (ftLib), savedFaceData (data, dataSize) { if (FT_New_Memory_Face (ftLib->library, (const FT_Byte*) savedFaceData.getData(), (FT_Long) savedFaceData.getSize(), faceIndex, &face) != 0) face = 0; } ~FTFaceWrapper() { if (face != 0) FT_Done_Face (face); } FT_Face face; FTLibWrapper::Ptr library; MemoryBlock savedFaceData; typedef ReferenceCountedObjectPtr Ptr; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper) }; //============================================================================== class FTTypefaceList : private DeletedAtShutdown { public: FTTypefaceList() : library (new FTLibWrapper()) { scanFontPaths (getDefaultFontDirectories()); } ~FTTypefaceList() { clearSingletonInstance(); } //============================================================================== struct KnownTypeface { KnownTypeface (const File& f, const int index, const FTFaceWrapper& face) : file (f), family (face.face->family_name), style (face.face->style_name), faceIndex (index), isMonospaced ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0), isSansSerif (isFaceSansSerif (family)) { } const File file; const String family, style; const int faceIndex; const bool isMonospaced, isSansSerif; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownTypeface) }; //============================================================================== static FTFaceWrapper::Ptr selectUnicodeCharmap (FTFaceWrapper* face) { if (face != nullptr) if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0) FT_Set_Charmap (face->face, face->face->charmaps[0]); return face; } FTFaceWrapper::Ptr createFace (const void* data, size_t dataSize, int index) { return selectUnicodeCharmap (new FTFaceWrapper (library, data, dataSize, index)); } FTFaceWrapper::Ptr createFace (const File& file, int index) { return selectUnicodeCharmap (new FTFaceWrapper (library, file, index)); } FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle) { const KnownTypeface* ftFace = matchTypeface (fontName, fontStyle); if (ftFace == nullptr) ftFace = matchTypeface (fontName, "Regular"); if (ftFace == nullptr) ftFace = matchTypeface (fontName, String()); if (ftFace != nullptr) return createFace (ftFace->file, ftFace->faceIndex); return nullptr; } //============================================================================== StringArray findAllFamilyNames() const { StringArray s; for (int i = 0; i < faces.size(); ++i) s.addIfNotAlreadyThere (faces.getUnchecked(i)->family); return s; } static int indexOfRegularStyle (const StringArray& styles) { int i = styles.indexOf ("Regular", true); if (i < 0) for (i = 0; i < styles.size(); ++i) if (! (styles[i].containsIgnoreCase ("Bold") || styles[i].containsIgnoreCase ("Italic"))) break; return i; } StringArray findAllTypefaceStyles (const String& family) const { StringArray s; for (int i = 0; i < faces.size(); ++i) { const KnownTypeface* const face = faces.getUnchecked(i); if (face->family == family) s.addIfNotAlreadyThere (face->style); } // try to get a regular style to be first in the list const int regular = indexOfRegularStyle (s); if (regular > 0) s.strings.swap (0, regular); return s; } void scanFontPaths (const StringArray& paths) { for (int i = 0; i < paths.size(); ++i) { DirectoryIterator iter (File::getCurrentWorkingDirectory() .getChildFile (paths[i]), true); while (iter.next()) if (iter.getFile().hasFileExtension ("ttf;pfb;pcf;otf")) scanFont (iter.getFile()); } } void getMonospacedNames (StringArray& monoSpaced) const { for (int i = 0; i < faces.size(); ++i) if (faces.getUnchecked(i)->isMonospaced) monoSpaced.addIfNotAlreadyThere (faces.getUnchecked(i)->family); } void getSerifNames (StringArray& serif) const { for (int i = 0; i < faces.size(); ++i) if (! faces.getUnchecked(i)->isSansSerif) serif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); } void getSansSerifNames (StringArray& sansSerif) const { for (int i = 0; i < faces.size(); ++i) if (faces.getUnchecked(i)->isSansSerif) sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); } juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList) private: FTLibWrapper::Ptr library; OwnedArray faces; static StringArray getDefaultFontDirectories(); void scanFont (const File& file) { int faceIndex = 0; int numFaces = 0; do { FTFaceWrapper face (library, file, faceIndex); if (face.face != 0) { if (faceIndex == 0) numFaces = (int) face.face->num_faces; if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) faces.add (new KnownTypeface (file, faceIndex, face)); } ++faceIndex; } while (faceIndex < numFaces); } const KnownTypeface* matchTypeface (const String& familyName, const String& style) const noexcept { for (int i = 0; i < faces.size(); ++i) { const KnownTypeface* const face = faces.getUnchecked(i); if (face->family == familyName && (face->style.equalsIgnoreCase (style) || style.isEmpty())) return face; } return nullptr; } static bool isFaceSansSerif (const String& family) { static const char* sansNames[] = { "Sans", "Verdana", "Arial", "Ubuntu" }; for (int i = 0; i < numElementsInArray (sansNames); ++i) if (family.containsIgnoreCase (sansNames[i])) return true; return false; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTTypefaceList) }; juce_ImplementSingleton_SingleThreaded (FTTypefaceList) //============================================================================== class FreeTypeTypeface : public CustomTypeface { public: FreeTypeTypeface (const Font& font) : faceWrapper (FTTypefaceList::getInstance()->createFace (font.getTypefaceName(), font.getTypefaceStyle())) { if (faceWrapper != nullptr) initialiseCharacteristics (font.getTypefaceName(), font.getTypefaceStyle()); } FreeTypeTypeface (const void* data, size_t dataSize) : faceWrapper (FTTypefaceList::getInstance()->createFace (data, dataSize, 0)) { if (faceWrapper != nullptr) initialiseCharacteristics (faceWrapper->face->family_name, faceWrapper->face->style_name); } void initialiseCharacteristics (const String& fontName, const String& fontStyle) { setCharacteristics (fontName, fontStyle, faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), L' '); } bool loadGlyphIfPossible (const juce_wchar character) { if (faceWrapper != nullptr) { FT_Face face = faceWrapper->face; const unsigned int glyphIndex = FT_Get_Char_Index (face, (FT_ULong) character); if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING) == 0 && face->glyph->format == ft_glyph_format_outline) { const float scale = 1.0f / (float) (face->ascender - face->descender); Path destShape; if (getGlyphShape (destShape, face->glyph->outline, scale)) { addGlyph (character, destShape, face->glyph->metrics.horiAdvance * scale); if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) addKerning (face, (uint32) character, glyphIndex); return true; } } } return false; } private: FTFaceWrapper::Ptr faceWrapper; bool getGlyphShape (Path& destShape, const FT_Outline& outline, const float scaleX) { const float scaleY = -scaleX; const short* const contours = outline.contours; const char* const tags = outline.tags; const FT_Vector* const points = outline.points; for (int c = 0; c < outline.n_contours; ++c) { const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1; const int endPoint = contours[c]; for (int p = startPoint; p <= endPoint; ++p) { const float x = scaleX * points[p].x; const float y = scaleY * points[p].y; if (p == startPoint) { if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) { float x2 = scaleX * points [endPoint].x; float y2 = scaleY * points [endPoint].y; if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On) { x2 = (x + x2) * 0.5f; y2 = (y + y2) * 0.5f; } destShape.startNewSubPath (x2, y2); } else { destShape.startNewSubPath (x, y); } } if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On) { if (p != startPoint) destShape.lineTo (x, y); } else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) { const int nextIndex = (p == endPoint) ? startPoint : p + 1; float x2 = scaleX * points [nextIndex].x; float y2 = scaleY * points [nextIndex].y; if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic) { x2 = (x + x2) * 0.5f; y2 = (y + y2) * 0.5f; } else { ++p; } destShape.quadraticTo (x, y, x2, y2); } else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic) { const int next1 = p + 1; const int next2 = (p == (endPoint - 1)) ? startPoint : (p + 2); if (p >= endPoint || FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) return false; const float x2 = scaleX * points [next1].x; const float y2 = scaleY * points [next1].y; const float x3 = scaleX * points [next2].x; const float y3 = scaleY * points [next2].y; destShape.cubicTo (x, y, x2, y2, x3, y3); p += 2; } } destShape.closeSubPath(); } return true; } void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex) { const float height = (float) (face->ascender - face->descender); uint32 rightGlyphIndex; FT_ULong rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); while (rightGlyphIndex != 0) { FT_Vector kerning; if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0 && kerning.x != 0) addKerningPair ((juce_wchar) character, (juce_wchar) rightCharCode, kerning.x / height); rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); } } JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface) }; libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp000066400000000000000000000136641320201440200317340ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ StringArray FTTypefaceList::getDefaultFontDirectories() { StringArray fontDirs; fontDirs.addTokens (String (CharPointer_UTF8 (getenv ("JUCE_FONT_PATH"))), ";,", ""); fontDirs.removeEmptyStrings (true); if (fontDirs.size() == 0) { const ScopedPointer fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf"))); if (fontsInfo != nullptr) { forEachXmlChildElementWithTagName (*fontsInfo, e, "dir") { String fontPath (e->getAllSubText().trim()); if (fontPath.isNotEmpty()) { if (e->getStringAttribute ("prefix") == "xdg") { String xdgDataHome (SystemStats::getEnvironmentVariable ("XDG_DATA_HOME", String())); if (xdgDataHome.trimStart().isEmpty()) xdgDataHome = "~/.local/share"; fontPath = File (xdgDataHome).getChildFile (fontPath).getFullPathName(); } fontDirs.add (fontPath); } } } } if (fontDirs.size() == 0) fontDirs.add ("/usr/X11R6/lib/X11/fonts"); fontDirs.removeDuplicates (false); return fontDirs; } Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return new FreeTypeTypeface (font); } Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) { return new FreeTypeTypeface (data, dataSize); } void Typeface::scanFolderForFonts (const File& folder) { FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName())); } StringArray Font::findAllTypefaceNames() { return FTTypefaceList::getInstance()->findAllFamilyNames(); } StringArray Font::findAllTypefaceStyles (const String& family) { return FTTypefaceList::getInstance()->findAllTypefaceStyles (family); } bool TextLayout::createNativeLayout (const AttributedString&) { return false; } //============================================================================== struct DefaultFontNames { DefaultFontNames() : defaultSans (getDefaultSansSerifFontName()), defaultSerif (getDefaultSerifFontName()), defaultFixed (getDefaultMonospacedFontName()) { } String getRealFontName (const String& faceName) const { if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans; if (faceName == Font::getDefaultSerifFontName()) return defaultSerif; if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed; return faceName; } String defaultSans, defaultSerif, defaultFixed; private: static String pickBestFont (const StringArray& names, const char* const* choicesArray) { const StringArray choices (choicesArray); for (int j = 0; j < choices.size(); ++j) if (names.contains (choices[j], true)) return choices[j]; for (int j = 0; j < choices.size(); ++j) for (int i = 0; i < names.size(); ++i) if (names[i].startsWithIgnoreCase (choices[j])) return names[i]; for (int j = 0; j < choices.size(); ++j) for (int i = 0; i < names.size(); ++i) if (names[i].containsIgnoreCase (choices[j])) return names[i]; return names[0]; } static String getDefaultSansSerifFontName() { StringArray allFonts; FTTypefaceList::getInstance()->getSansSerifNames (allFonts); static const char* targets[] = { "Verdana", "Bitstream Vera Sans", "Luxi Sans", "Liberation Sans", "DejaVu Sans", "Sans", nullptr }; return pickBestFont (allFonts, targets); } static String getDefaultSerifFontName() { StringArray allFonts; FTTypefaceList::getInstance()->getSerifNames (allFonts); static const char* targets[] = { "Bitstream Vera Serif", "Times", "Nimbus Roman", "Liberation Serif", "DejaVu Serif", "Serif", nullptr }; return pickBestFont (allFonts, targets); } static String getDefaultMonospacedFontName() { StringArray allFonts; FTTypefaceList::getInstance()->getMonospacedNames (allFonts); static const char* targets[] = { "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Sans Mono", "Liberation Mono", "Courier", "DejaVu Mono", "Mono", nullptr }; return pickBestFont (allFonts, targets); } JUCE_DECLARE_NON_COPYABLE (DefaultFontNames) }; Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; Font f (font); f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName())); return Typeface::createSystemTypefaceFor (f); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h000066400000000000000000000110441320201440200336150ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MAC_COREGRAPHICSCONTEXT_H_INCLUDED #define JUCE_MAC_COREGRAPHICSCONTEXT_H_INCLUDED //============================================================================== class CoreGraphicsContext : public LowLevelGraphicsContext { public: CoreGraphicsContext (CGContextRef context, const float flipHeight, const float targetScale); ~CoreGraphicsContext(); //============================================================================== bool isVectorDevice() const override { return false; } void setOrigin (Point) override; void addTransform (const AffineTransform&) override; float getPhysicalPixelScaleFactor() override; bool clipToRectangle (const Rectangle&) override; bool clipToRectangleList (const RectangleList&) override; void excludeClipRectangle (const Rectangle&) override; void clipToPath (const Path&, const AffineTransform&) override; void clipToImageAlpha (const Image&, const AffineTransform&) override; bool clipRegionIntersects (const Rectangle&) override; Rectangle getClipBounds() const override; bool isClipEmpty() const override; //============================================================================== void saveState() override; void restoreState() override; void beginTransparencyLayer (float opacity) override; void endTransparencyLayer() override; //============================================================================== void setFill (const FillType&) override; void setOpacity (float) override; void setInterpolationQuality (Graphics::ResamplingQuality) override; //============================================================================== void fillRect (const Rectangle&, bool replaceExistingContents) override; void fillRect (const Rectangle&) override; void fillRectList (const RectangleList&) override; void fillPath (const Path&, const AffineTransform&) override; void drawImage (const Image& sourceImage, const AffineTransform&) override; //============================================================================== void drawLine (const Line&) override; void setFont (const Font&) override; const Font& getFont() override; void drawGlyph (int glyphNumber, const AffineTransform&) override; bool drawTextLayout (const AttributedString&, const Rectangle&) override; private: CGContextRef context; const CGFloat flipHeight; float targetScale; CGColorSpaceRef rgbColourSpace, greyColourSpace; mutable Rectangle lastClipRect; mutable bool lastClipRectIsValid; struct SavedState { SavedState(); SavedState (const SavedState&); ~SavedState(); void setFill (const FillType&); FillType fillType; Font font; CGFontRef fontRef; CGAffineTransform fontTransform; CGGradientRef gradient; }; ScopedPointer state; OwnedArray stateStack; void drawGradient(); void createPath (const Path&) const; void createPath (const Path&, const AffineTransform&) const; void flip() const; void applyTransform (const AffineTransform&) const; void drawImage (const Image&, const AffineTransform&, bool fillEntireClipAsTiles); bool clipToRectangleListWithoutTest (const RectangleList&); void fillCGRect (const CGRect&, bool replaceExistingContents); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsContext) }; #endif // JUCE_MAC_COREGRAPHICSCONTEXT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm000066400000000000000000000766671320201440200340250ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #include "juce_mac_CoreGraphicsContext.h" //============================================================================== class CoreGraphicsImage : public ImagePixelData { public: CoreGraphicsImage (const Image::PixelFormat format, const int w, const int h, const bool clearImage) : ImagePixelData (format, w, h), cachedImageRef (0) { pixelStride = format == Image::RGB ? 3 : ((format == Image::ARGB) ? 4 : 1); lineStride = (pixelStride * jmax (1, width) + 3) & ~3; imageData.allocate ((size_t) (lineStride * jmax (1, height)), clearImage); CGColorSpaceRef colourSpace = (format == Image::SingleChannel) ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB(); context = CGBitmapContextCreate (imageData, (size_t) width, (size_t) height, 8, (size_t) lineStride, colourSpace, getCGImageFlags (format)); CGColorSpaceRelease (colourSpace); } ~CoreGraphicsImage() { freeCachedImageRef(); CGContextRelease (context); } LowLevelGraphicsContext* createLowLevelContext() override { freeCachedImageRef(); sendDataChangeMessage(); return new CoreGraphicsContext (context, height, 1.0f); } void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; if (mode != Image::BitmapData::readOnly) { freeCachedImageRef(); sendDataChangeMessage(); } } ImagePixelData* clone() override { CoreGraphicsImage* im = new CoreGraphicsImage (pixelFormat, width, height, false); memcpy (im->imageData, imageData, (size_t) (lineStride * height)); return im; } ImageType* createType() const override { return new NativeImageType(); } //============================================================================== static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace) { CoreGraphicsImage* const cgim = dynamic_cast (juceImage.getPixelData()); if (cgim != nullptr && cgim->cachedImageRef != 0) { CGImageRetain (cgim->cachedImageRef); return cgim->cachedImageRef; } CGImageRef ref = createImage (juceImage, colourSpace, false); if (cgim != nullptr) { CGImageRetain (ref); cgim->cachedImageRef = ref; } return ref; } static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpace, const bool mustOutliveSource) { const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly); CGDataProviderRef provider; if (mustOutliveSource) { CFDataRef data = CFDataCreate (0, (const UInt8*) srcData.data, (CFIndex) (srcData.lineStride * srcData.height)); provider = CGDataProviderCreateWithCFData (data); CFRelease (data); } else { provider = CGDataProviderCreateWithData (0, srcData.data, (size_t) (srcData.lineStride * srcData.height), 0); } CGImageRef imageRef = CGImageCreate ((size_t) srcData.width, (size_t) srcData.height, 8, (size_t) srcData.pixelStride * 8, (size_t) srcData.lineStride, colourSpace, getCGImageFlags (juceImage.getFormat()), provider, 0, true, kCGRenderingIntentDefault); CGDataProviderRelease (provider); return imageRef; } //============================================================================== CGContextRef context; CGImageRef cachedImageRef; HeapBlock imageData; int pixelStride, lineStride; private: void freeCachedImageRef() { if (cachedImageRef != 0) { CGImageRelease (cachedImageRef); cachedImageRef = 0; } } static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) { #if JUCE_BIG_ENDIAN return format == Image::ARGB ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big) : kCGBitmapByteOrderDefault; #else return format == Image::ARGB ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) : kCGBitmapByteOrderDefault; #endif } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsImage) }; ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { return new CoreGraphicsImage (format == Image::RGB ? Image::ARGB : format, width, height, clearImage); } //============================================================================== CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, const float h, const float scale) : context (c), flipHeight (h), targetScale (scale), lastClipRectIsValid (false), state (new SavedState()) { CGContextRetain (context); CGContextSaveGState(context); CGContextSetShouldSmoothFonts (context, true); CGContextSetAllowsFontSmoothing (context, true); CGContextSetShouldAntialias (context, true); CGContextSetBlendMode (context, kCGBlendModeNormal); rgbColourSpace = CGColorSpaceCreateDeviceRGB(); greyColourSpace = CGColorSpaceCreateDeviceGray(); setFont (Font()); } CoreGraphicsContext::~CoreGraphicsContext() { CGContextRestoreGState (context); CGContextRelease (context); CGColorSpaceRelease (rgbColourSpace); CGColorSpaceRelease (greyColourSpace); } //============================================================================== void CoreGraphicsContext::setOrigin (Point o) { CGContextTranslateCTM (context, o.x, -o.y); if (lastClipRectIsValid) lastClipRect.translate (-o.x, -o.y); } void CoreGraphicsContext::addTransform (const AffineTransform& transform) { applyTransform (AffineTransform::verticalFlip ((float) flipHeight) .followedBy (transform) .translated (0, (float) -flipHeight) .scaled (1.0f, -1.0f)); lastClipRectIsValid = false; jassert (getPhysicalPixelScaleFactor() > 0.0f); jassert (getPhysicalPixelScaleFactor() > 0.0f); } float CoreGraphicsContext::getPhysicalPixelScaleFactor() { const CGAffineTransform t = CGContextGetCTM (context); return targetScale * (float) (juce_hypot (t.a, t.c) + juce_hypot (t.b, t.d)) / 2.0f; } bool CoreGraphicsContext::clipToRectangle (const Rectangle& r) { CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); if (lastClipRectIsValid) { // This is actually incorrect, because the actual clip region may be complex, and // clipping its bounds to a rect may not be right... But, removing this shortcut // doesn't actually fix anything because CoreGraphics also ignores complex regions // when calculating the resultant clip bounds, and makes the same mistake! lastClipRect = lastClipRect.getIntersection (r); return ! lastClipRect.isEmpty(); } return ! isClipEmpty(); } bool CoreGraphicsContext::clipToRectangleListWithoutTest (const RectangleList& clipRegion) { if (clipRegion.isEmpty()) { CGContextClipToRect (context, CGRectZero); lastClipRectIsValid = true; lastClipRect = Rectangle(); return false; } const size_t numRects = (size_t) clipRegion.getNumRectangles(); HeapBlock rects (numRects); int i = 0; for (const Rectangle* r = clipRegion.begin(), * const e = clipRegion.end(); r != e; ++r) rects[i++] = CGRectMake (r->getX(), flipHeight - r->getBottom(), r->getWidth(), r->getHeight()); CGContextClipToRects (context, rects, numRects); lastClipRectIsValid = false; return true; } bool CoreGraphicsContext::clipToRectangleList (const RectangleList& clipRegion) { return clipToRectangleListWithoutTest (clipRegion) && ! isClipEmpty(); } void CoreGraphicsContext::excludeClipRectangle (const Rectangle& r) { RectangleList remaining (getClipBounds()); remaining.subtract (r); clipToRectangleListWithoutTest (remaining); } void CoreGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform) { createPath (path, transform); CGContextClip (context); lastClipRectIsValid = false; } void CoreGraphicsContext::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) { if (! transform.isSingularity()) { Image singleChannelImage (sourceImage); if (sourceImage.getFormat() != Image::SingleChannel) singleChannelImage = sourceImage.convertedToFormat (Image::SingleChannel); CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, greyColourSpace, true); flip(); AffineTransform t (AffineTransform::verticalFlip (sourceImage.getHeight()).followedBy (transform)); applyTransform (t); CGRect r = convertToCGRect (sourceImage.getBounds()); CGContextClipToMask (context, r, image); applyTransform (t.inverted()); flip(); CGImageRelease (image); lastClipRectIsValid = false; } } bool CoreGraphicsContext::clipRegionIntersects (const Rectangle& r) { return getClipBounds().intersects (r); } Rectangle CoreGraphicsContext::getClipBounds() const { if (! lastClipRectIsValid) { CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); lastClipRectIsValid = true; lastClipRect.setBounds (roundToInt (bounds.origin.x), roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), roundToInt (bounds.size.width), roundToInt (bounds.size.height)); } return lastClipRect; } bool CoreGraphicsContext::isClipEmpty() const { return getClipBounds().isEmpty(); } //============================================================================== void CoreGraphicsContext::saveState() { CGContextSaveGState (context); stateStack.add (new SavedState (*state)); } void CoreGraphicsContext::restoreState() { CGContextRestoreGState (context); if (SavedState* const top = stateStack.getLast()) { state = top; stateStack.removeLast (1, false); lastClipRectIsValid = false; } else { jassertfalse; // trying to pop with an empty stack! } } void CoreGraphicsContext::beginTransparencyLayer (float opacity) { saveState(); CGContextSetAlpha (context, opacity); CGContextBeginTransparencyLayer (context, 0); } void CoreGraphicsContext::endTransparencyLayer() { CGContextEndTransparencyLayer (context); restoreState(); } //============================================================================== void CoreGraphicsContext::setFill (const FillType& fillType) { state->setFill (fillType); if (fillType.isColour()) { CGContextSetRGBFillColor (context, fillType.colour.getFloatRed(), fillType.colour.getFloatGreen(), fillType.colour.getFloatBlue(), fillType.colour.getFloatAlpha()); CGContextSetAlpha (context, 1.0f); } } void CoreGraphicsContext::setOpacity (float newOpacity) { state->fillType.setOpacity (newOpacity); setFill (state->fillType); } void CoreGraphicsContext::setInterpolationQuality (Graphics::ResamplingQuality quality) { CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality ? kCGInterpolationLow : kCGInterpolationHigh); } //============================================================================== void CoreGraphicsContext::fillRect (const Rectangle& r, const bool replaceExistingContents) { fillCGRect (CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()), replaceExistingContents); } void CoreGraphicsContext::fillRect (const Rectangle& r) { fillCGRect (CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()), false); } void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, const bool replaceExistingContents) { if (replaceExistingContents) { #if JUCE_IOS CGContextSetBlendMode (context, kCGBlendModeCopy); #elif MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 CGContextClearRect (context, cgRect); #else #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (CGContextDrawLinearGradient == 0) // (just a way of checking whether we're running in 10.5 or later) CGContextClearRect (context, cgRect); else #endif CGContextSetBlendMode (context, kCGBlendModeCopy); #endif fillCGRect (cgRect, false); CGContextSetBlendMode (context, kCGBlendModeNormal); } else { if (state->fillType.isColour()) { CGContextFillRect (context, cgRect); } else if (state->fillType.isGradient()) { CGContextSaveGState (context); CGContextClipToRect (context, cgRect); drawGradient(); CGContextRestoreGState (context); } else { CGContextSaveGState (context); CGContextClipToRect (context, cgRect); drawImage (state->fillType.image, state->fillType.transform, true); CGContextRestoreGState (context); } } } void CoreGraphicsContext::fillPath (const Path& path, const AffineTransform& transform) { CGContextSaveGState (context); if (state->fillType.isColour()) { flip(); applyTransform (transform); createPath (path); if (path.isUsingNonZeroWinding()) CGContextFillPath (context); else CGContextEOFillPath (context); } else { createPath (path, transform); if (path.isUsingNonZeroWinding()) CGContextClip (context); else CGContextEOClip (context); if (state->fillType.isGradient()) drawGradient(); else drawImage (state->fillType.image, state->fillType.transform, true); } CGContextRestoreGState (context); } void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform) { drawImage (sourceImage, transform, false); } void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform, const bool fillEntireClipAsTiles) { const int iw = sourceImage.getWidth(); const int ih = sourceImage.getHeight(); CGImageRef image = CoreGraphicsImage::getCachedImageRef (sourceImage, rgbColourSpace); CGContextSaveGState (context); CGContextSetAlpha (context, state->fillType.getOpacity()); flip(); applyTransform (AffineTransform::verticalFlip (ih).followedBy (transform)); CGRect imageRect = CGRectMake (0, 0, iw, ih); if (fillEntireClipAsTiles) { #if JUCE_IOS CGContextDrawTiledImage (context, imageRect, image); #else #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 // There's a bug in CGContextDrawTiledImage that makes it incredibly slow // if it's doing a transformation - it's quicker to just draw lots of images manually if (&CGContextDrawTiledImage != 0 && transform.isOnlyTranslation()) CGContextDrawTiledImage (context, imageRect, image); else #endif { // Fallback to manually doing a tiled fill on 10.4 CGRect clip = CGRectIntegral (CGContextGetClipBoundingBox (context)); int x = 0, y = 0; while (x > clip.origin.x) x -= iw; while (y > clip.origin.y) y -= ih; const int right = (int) (clip.origin.x + clip.size.width); const int bottom = (int) (clip.origin.y + clip.size.height); while (y < bottom) { for (int x2 = x; x2 < right; x2 += iw) CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image); y += ih; } } #endif } else { CGContextDrawImage (context, imageRect, image); } CGImageRelease (image); // (This causes a memory bug in iOS sim 3.0 - try upgrading to a later version if you hit this) CGContextRestoreGState (context); } //============================================================================== void CoreGraphicsContext::drawLine (const Line& line) { if (state->fillType.isColour()) { CGContextSetLineCap (context, kCGLineCapSquare); CGContextSetLineWidth (context, 1.0f); CGContextSetRGBStrokeColor (context, state->fillType.colour.getFloatRed(), state->fillType.colour.getFloatGreen(), state->fillType.colour.getFloatBlue(), state->fillType.colour.getFloatAlpha()); CGPoint cgLine[] = { { (CGFloat) line.getStartX(), flipHeight - (CGFloat) line.getStartY() }, { (CGFloat) line.getEndX(), flipHeight - (CGFloat) line.getEndY() } }; CGContextStrokeLineSegments (context, cgLine, 1); } else { Path p; p.addLineSegment (line, 1.0f); fillPath (p, AffineTransform::identity); } } void CoreGraphicsContext::fillRectList (const RectangleList& list) { HeapBlock rects ((size_t) list.getNumRectangles()); size_t num = 0; for (const Rectangle* r = list.begin(), * const e = list.end(); r != e; ++r) rects[num++] = CGRectMake (r->getX(), flipHeight - r->getBottom(), r->getWidth(), r->getHeight()); if (state->fillType.isColour()) { CGContextFillRects (context, rects, num); } else if (state->fillType.isGradient()) { CGContextSaveGState (context); CGContextClipToRects (context, rects, num); drawGradient(); CGContextRestoreGState (context); } else { CGContextSaveGState (context); CGContextClipToRects (context, rects, num); drawImage (state->fillType.image, state->fillType.transform, true); CGContextRestoreGState (context); } } void CoreGraphicsContext::setFont (const Font& newFont) { if (state->font != newFont) { state->fontRef = 0; state->font = newFont; if (OSXTypeface* osxTypeface = dynamic_cast (state->font.getTypeface())) { state->fontRef = osxTypeface->fontRef; CGContextSetFont (context, state->fontRef); CGContextSetFontSize (context, state->font.getHeight() * osxTypeface->fontHeightToPointsFactor); state->fontTransform = osxTypeface->renderingTransform; state->fontTransform.a *= state->font.getHorizontalScale(); CGContextSetTextMatrix (context, state->fontTransform); } } } const Font& CoreGraphicsContext::getFont() { return state->font; } void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& transform) { if (state->fontRef != 0 && state->fillType.isColour()) { #if JUCE_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif if (transform.isOnlyTranslation()) { CGContextSetTextMatrix (context, state->fontTransform); // have to set this each time, as it's not saved as part of the state CGGlyph g = (CGGlyph) glyphNumber; CGContextShowGlyphsAtPoint (context, transform.getTranslationX(), flipHeight - roundToInt (transform.getTranslationY()), &g, 1); } else { CGContextSaveGState (context); flip(); applyTransform (transform); CGAffineTransform t = state->fontTransform; t.d = -t.d; CGContextSetTextMatrix (context, t); CGGlyph g = (CGGlyph) glyphNumber; CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); CGContextRestoreGState (context); } #if JUCE_CLANG #pragma clang diagnostic pop #endif } else { Path p; Font& f = state->font; f.getTypeface()->getOutlineForGlyph (glyphNumber, p); fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()) .followedBy (transform)); } } bool CoreGraphicsContext::drawTextLayout (const AttributedString& text, const Rectangle& area) { #if JUCE_CORETEXT_AVAILABLE CoreTextTypeLayout::drawToCGContext (text, area, context, (float) flipHeight); return true; #else (void) text; (void) area; return false; #endif } CoreGraphicsContext::SavedState::SavedState() : font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity), gradient (0) { } CoreGraphicsContext::SavedState::SavedState (const SavedState& other) : fillType (other.fillType), font (other.font), fontRef (other.fontRef), fontTransform (other.fontTransform), gradient (other.gradient) { if (gradient != 0) CGGradientRetain (gradient); } CoreGraphicsContext::SavedState::~SavedState() { if (gradient != 0) CGGradientRelease (gradient); } void CoreGraphicsContext::SavedState::setFill (const FillType& newFill) { fillType = newFill; if (gradient != 0) { CGGradientRelease (gradient); gradient = 0; } } static CGGradientRef createGradient (const ColourGradient& g, CGColorSpaceRef colourSpace) { const int numColours = g.getNumColours(); CGFloat* const data = (CGFloat*) alloca ((size_t) numColours * 5 * sizeof (CGFloat)); CGFloat* const locations = data; CGFloat* const components = data + numColours; CGFloat* comps = components; for (int i = 0; i < numColours; ++i) { const Colour colour (g.getColour (i)); *comps++ = (CGFloat) colour.getFloatRed(); *comps++ = (CGFloat) colour.getFloatGreen(); *comps++ = (CGFloat) colour.getFloatBlue(); *comps++ = (CGFloat) colour.getFloatAlpha(); locations[i] = (CGFloat) g.getColourPosition (i); } return CGGradientCreateWithColorComponents (colourSpace, components, locations, (size_t) numColours); } void CoreGraphicsContext::drawGradient() { flip(); applyTransform (state->fillType.transform); CGContextSetAlpha (context, state->fillType.getOpacity()); const ColourGradient& g = *state->fillType.gradient; CGPoint p1 (convertToCGPoint (g.point1)); CGPoint p2 (convertToCGPoint (g.point2)); state->fillType.transform.transformPoints (p1.x, p1.y, p2.x, p2.y); if (state->gradient == 0) state->gradient = createGradient (g, rgbColourSpace); if (g.isRadial) CGContextDrawRadialGradient (context, state->gradient, p1, 0, p1, g.point1.getDistanceFrom (g.point2), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); else CGContextDrawLinearGradient (context, state->gradient, p1, p2, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); } void CoreGraphicsContext::createPath (const Path& path) const { CGContextBeginPath (context); for (Path::Iterator i (path); i.next();) { switch (i.elementType) { case Path::Iterator::startNewSubPath: CGContextMoveToPoint (context, i.x1, i.y1); break; case Path::Iterator::lineTo: CGContextAddLineToPoint (context, i.x1, i.y1); break; case Path::Iterator::quadraticTo: CGContextAddQuadCurveToPoint (context, i.x1, i.y1, i.x2, i.y2); break; case Path::Iterator::cubicTo: CGContextAddCurveToPoint (context, i.x1, i.y1, i.x2, i.y2, i.x3, i.y3); break; case Path::Iterator::closePath: CGContextClosePath (context); break; default: jassertfalse; break; } } } void CoreGraphicsContext::createPath (const Path& path, const AffineTransform& transform) const { CGContextBeginPath (context); for (Path::Iterator i (path); i.next();) { switch (i.elementType) { case Path::Iterator::startNewSubPath: transform.transformPoint (i.x1, i.y1); CGContextMoveToPoint (context, i.x1, flipHeight - i.y1); break; case Path::Iterator::lineTo: transform.transformPoint (i.x1, i.y1); CGContextAddLineToPoint (context, i.x1, flipHeight - i.y1); break; case Path::Iterator::quadraticTo: transform.transformPoints (i.x1, i.y1, i.x2, i.y2); CGContextAddQuadCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2); break; case Path::Iterator::cubicTo: transform.transformPoints (i.x1, i.y1, i.x2, i.y2, i.x3, i.y3); CGContextAddCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2, i.x3, flipHeight - i.y3); break; case Path::Iterator::closePath: CGContextClosePath (context); break; default: jassertfalse; break; } } } void CoreGraphicsContext::flip() const { CGContextConcatCTM (context, CGAffineTransformMake (1, 0, 0, -1, 0, flipHeight)); } void CoreGraphicsContext::applyTransform (const AffineTransform& transform) const { CGAffineTransform t; t.a = transform.mat00; t.b = transform.mat10; t.c = transform.mat01; t.d = transform.mat11; t.tx = transform.mat02; t.ty = transform.mat12; CGContextConcatCTM (context, t); } //============================================================================== #if USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER Image juce_loadWithCoreImage (InputStream& input) { MemoryBlock data; input.readIntoMemoryBlock (data, -1); #if JUCE_IOS JUCE_AUTORELEASEPOOL #endif { #if JUCE_IOS if (UIImage* uiImage = [UIImage imageWithData: [NSData dataWithBytesNoCopy: data.getData() length: data.getSize() freeWhenDone: NO]]) { CGImageRef loadedImage = uiImage.CGImage; #else CGDataProviderRef provider = CGDataProviderCreateWithData (0, data.getData(), data.getSize(), 0); CGImageSourceRef imageSource = CGImageSourceCreateWithDataProvider (provider, 0); CGDataProviderRelease (provider); if (imageSource != 0) { CGImageRef loadedImage = CGImageSourceCreateImageAtIndex (imageSource, 0, 0); CFRelease (imageSource); #endif if (loadedImage != 0) { CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo (loadedImage); const bool hasAlphaChan = (alphaInfo != kCGImageAlphaNone && alphaInfo != kCGImageAlphaNoneSkipLast && alphaInfo != kCGImageAlphaNoneSkipFirst); Image image (NativeImageType().create (Image::ARGB, // (CoreImage doesn't work with 24-bit images) (int) CGImageGetWidth (loadedImage), (int) CGImageGetHeight (loadedImage), hasAlphaChan)); CoreGraphicsImage* const cgImage = dynamic_cast (image.getPixelData()); jassert (cgImage != nullptr); // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsImage class should have been used. CGContextDrawImage (cgImage->context, convertToCGRect (image.getBounds()), loadedImage); CGContextFlush (cgImage->context); #if ! JUCE_IOS CFRelease (loadedImage); #endif // Because it's impossible to create a truly 24-bit CG image, this flag allows a user // to find out whether the file they just loaded the image from had an alpha channel or not. image.getProperties()->set ("originalImageHadAlpha", hasAlphaChan); return image; } } } return Image::null; } #endif #if JUCE_MAC Image juce_createImageFromCIImage (CIImage*, int, int); Image juce_createImageFromCIImage (CIImage* im, int w, int h) { CoreGraphicsImage* cgImage = new CoreGraphicsImage (Image::ARGB, w, h, false); CIContext* cic = [CIContext contextWithCGContext: cgImage->context options: nil]; [cic drawImage: im inRect: CGRectMake (0, 0, w, h) fromRect: CGRectMake (0, 0, w, h)]; CGContextFlush (cgImage->context); return Image (cgImage); } CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef colourSpace, const bool mustOutliveSource) { return CoreGraphicsImage::createImage (juceImage, colourSpace, mustOutliveSource); } CGContextRef juce_getImageContext (const Image& image) { if (CoreGraphicsImage* const cgi = dynamic_cast (image.getPixelData())) return cgi->context; jassertfalse; return 0; } #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h000066400000000000000000000041371320201440200336000ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MAC_COREGRAPHICSHELPERS_H_INCLUDED #define JUCE_MAC_COREGRAPHICSHELPERS_H_INCLUDED //============================================================================== namespace { template Rectangle convertToRectInt (RectType r) noexcept { return Rectangle ((int) r.origin.x, (int) r.origin.y, (int) r.size.width, (int) r.size.height); } template Rectangle convertToRectFloat (RectType r) noexcept { return Rectangle (r.origin.x, r.origin.y, r.size.width, r.size.height); } template CGRect convertToCGRect (RectType r) noexcept { return CGRectMake ((CGFloat) r.getX(), (CGFloat) r.getY(), (CGFloat) r.getWidth(), (CGFloat) r.getHeight()); } template CGPoint convertToCGPoint (PointType p) noexcept { return CGPointMake ((CGFloat) p.x, (CGFloat) p.y); } } extern CGImageRef juce_createCoreGraphicsImage (const Image&, CGColorSpaceRef, bool mustOutliveSource); extern CGContextRef juce_getImageContext (const Image&); #endif // JUCE_MAC_COREGRAPHICSHELPERS_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm000066400000000000000000001433541320201440200311640ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if (! defined (JUCE_CORETEXT_AVAILABLE)) \ && (JUCE_IOS || (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4)) #define JUCE_CORETEXT_AVAILABLE 1 #endif const float referenceFontSize = 1024.0f; #if JUCE_CORETEXT_AVAILABLE #if JUCE_MAC && MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5 extern "C" { void CTRunGetAdvances (CTRunRef, CFRange, CGSize buffer[]); const CGSize* CTRunGetAdvancesPtr (CTRunRef); } #endif static CTFontRef getCTFontFromTypeface (const Font& f); namespace CoreTextTypeLayout { static String findBestAvailableStyle (const Font& font, CGAffineTransform& requiredTransform) { const StringArray availableStyles (Font::findAllTypefaceStyles (font.getTypefaceName())); const String style (font.getTypefaceStyle()); if (! availableStyles.contains (style)) { if (font.isItalic()) // Fake-up an italic font if there isn't a real one. requiredTransform = CGAffineTransformMake (1.0f, 0, 0.25f, 1.0f, 0, 0); return availableStyles[0]; } return style; } // Workaround for Apple bug in CTFontCreateWithFontDescriptor in Garageband/Logic on 10.6 #if JUCE_MAC && ((! defined (MAC_OS_X_VERSION_10_7)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) static CTFontRef getFontWithTrait (CTFontRef ctFontRef, CTFontSymbolicTraits trait) { if (CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr, trait, trait)) { CFRelease (ctFontRef); return newFont; } return ctFontRef; } static CTFontRef useStyleFallbackIfNecessary (CTFontRef ctFontRef, CFStringRef cfFontFamily, const float fontSizePoints, const Font& font) { CFStringRef cfActualFontFamily = (CFStringRef) CTFontCopyAttribute (ctFontRef, kCTFontFamilyNameAttribute); if (CFStringCompare (cfFontFamily, cfActualFontFamily, 0) != kCFCompareEqualTo) { CFRelease (ctFontRef); ctFontRef = CTFontCreateWithName (cfFontFamily, fontSizePoints, nullptr); if (font.isItalic()) ctFontRef = getFontWithTrait (ctFontRef, kCTFontItalicTrait); if (font.isBold()) ctFontRef = getFontWithTrait (ctFontRef, kCTFontBoldTrait); } CFRelease (cfActualFontFamily); return ctFontRef; } #endif static float getFontTotalHeight (CTFontRef font) { return std::abs ((float) CTFontGetAscent (font)) + std::abs ((float) CTFontGetDescent (font)); } static float getHeightToPointsFactor (CTFontRef font) { return referenceFontSize / getFontTotalHeight (font); } static CTFontRef getFontWithPointSize (CTFontRef font, float size) { CTFontRef newFont = CTFontCreateCopyWithAttributes (font, size, nullptr, nullptr); CFRelease (font); return newFont; } static CTFontRef createCTFont (const Font& font, const float fontSizePoints, CGAffineTransform& transformRequired) { CFStringRef cfFontFamily = FontStyleHelpers::getConcreteFamilyName (font).toCFString(); CFStringRef cfFontStyle = findBestAvailableStyle (font, transformRequired).toCFString(); CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute }; CFTypeRef values[] = { cfFontFamily, cfFontStyle }; CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease (cfFontStyle); CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes); CFRelease (fontDescAttributes); CTFontRef ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, fontSizePoints, nullptr); CFRelease (ctFontDescRef); #if JUCE_MAC && ((! defined (MAC_OS_X_VERSION_10_7)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) ctFontRef = useStyleFallbackIfNecessary (ctFontRef, cfFontFamily, fontSizePoints, font); #endif CFRelease (cfFontFamily); return ctFontRef; } //============================================================================== struct Advances { Advances (CTRunRef run, const CFIndex numGlyphs) : advances (CTRunGetAdvancesPtr (run)) { if (advances == nullptr) { local.malloc ((size_t) numGlyphs); CTRunGetAdvances (run, CFRangeMake (0, 0), local); advances = local; } } const CGSize* advances; HeapBlock local; }; struct Glyphs { Glyphs (CTRunRef run, const size_t numGlyphs) : glyphs (CTRunGetGlyphsPtr (run)) { if (glyphs == nullptr) { local.malloc (numGlyphs); CTRunGetGlyphs (run, CFRangeMake (0, 0), local); glyphs = local; } } const CGGlyph* glyphs; HeapBlock local; }; struct Positions { Positions (CTRunRef run, const size_t numGlyphs) : points (CTRunGetPositionsPtr (run)) { if (points == nullptr) { local.malloc (numGlyphs); CTRunGetPositions (run, CFRangeMake (0, 0), local); points = local; } } const CGPoint* points; HeapBlock local; }; struct LineInfo { LineInfo (CTFrameRef frame, CTLineRef line, CFIndex lineIndex) { CTFrameGetLineOrigins (frame, CFRangeMake (lineIndex, 1), &origin); CTLineGetTypographicBounds (line, &ascent, &descent, &leading); } CGPoint origin; CGFloat ascent, descent, leading; }; static CTFontRef getOrCreateFont (const Font& f) { if (CTFontRef ctf = getCTFontFromTypeface (f)) { CFRetain (ctf); return ctf; } CGAffineTransform transform; return createCTFont (f, referenceFontSize, transform); } //============================================================================== static CFAttributedStringRef createCFAttributedString (const AttributedString& text) { #if JUCE_IOS CGColorSpaceRef rgbColourSpace = CGColorSpaceCreateDeviceRGB(); #endif CFStringRef cfText = text.getText().toCFString(); CFMutableAttributedStringRef attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0); CFAttributedStringReplaceString (attribString, CFRangeMake(0, 0), cfText); CFRelease (cfText); const int numCharacterAttributes = text.getNumAttributes(); for (int i = 0; i < numCharacterAttributes; ++i) { const AttributedString::Attribute& attr = *text.getAttribute (i); if (attr.range.getStart() > CFAttributedStringGetLength (attribString)) continue; Range range (attr.range); range.setEnd (jmin (range.getEnd(), (int) CFAttributedStringGetLength (attribString))); if (const Font* const f = attr.getFont()) { if (CTFontRef ctFontRef = getOrCreateFont (*f)) { ctFontRef = getFontWithPointSize (ctFontRef, f->getHeight() * getHeightToPointsFactor (ctFontRef)); CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), kCTFontAttributeName, ctFontRef); CFRelease (ctFontRef); } } if (const Colour* const col = attr.getColour()) { #if JUCE_IOS const CGFloat components[] = { col->getFloatRed(), col->getFloatGreen(), col->getFloatBlue(), col->getFloatAlpha() }; CGColorRef colour = CGColorCreate (rgbColourSpace, components); #else CGColorRef colour = CGColorCreateGenericRGB (col->getFloatRed(), col->getFloatGreen(), col->getFloatBlue(), col->getFloatAlpha()); #endif CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), kCTForegroundColorAttributeName, colour); CGColorRelease (colour); } } // Paragraph Attributes CTTextAlignment ctTextAlignment = kCTLeftTextAlignment; CTLineBreakMode ctLineBreakMode = kCTLineBreakByWordWrapping; const CGFloat ctLineSpacing = text.getLineSpacing(); switch (text.getJustification().getOnlyHorizontalFlags()) { case Justification::left: break; case Justification::right: ctTextAlignment = kCTRightTextAlignment; break; case Justification::horizontallyCentred: ctTextAlignment = kCTCenterTextAlignment; break; case Justification::horizontallyJustified: ctTextAlignment = kCTJustifiedTextAlignment; break; default: jassertfalse; break; // Illegal justification flags } switch (text.getWordWrap()) { case AttributedString::byWord: break; case AttributedString::none: ctLineBreakMode = kCTLineBreakByClipping; break; case AttributedString::byChar: ctLineBreakMode = kCTLineBreakByCharWrapping; break; default: break; } CTParagraphStyleSetting settings[] = { { kCTParagraphStyleSpecifierAlignment, sizeof (CTTextAlignment), &ctTextAlignment }, { kCTParagraphStyleSpecifierLineBreakMode, sizeof (CTLineBreakMode), &ctLineBreakMode }, #if defined (MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 { kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &ctLineSpacing } #else { kCTParagraphStyleSpecifierLineSpacing, sizeof (CGFloat), &ctLineSpacing } #endif }; CTParagraphStyleRef ctParagraphStyleRef = CTParagraphStyleCreate (settings, (size_t) numElementsInArray (settings)); CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)), kCTParagraphStyleAttributeName, ctParagraphStyleRef); CFRelease (ctParagraphStyleRef); #if JUCE_IOS CGColorSpaceRelease (rgbColourSpace); #endif return attribString; } static CTFrameRef createCTFrame (const AttributedString& text, CGRect bounds) { CFAttributedStringRef attribString = createCFAttributedString (text); CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString (attribString); CFRelease (attribString); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect (path, nullptr, bounds); CTFrameRef frame = CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr); CFRelease (framesetter); CGPathRelease (path); return frame; } static Range getLineVerticalRange (CTFrameRef frame, CFArrayRef lines, int lineIndex) { LineInfo info (frame, (CTLineRef) CFArrayGetValueAtIndex (lines, lineIndex), lineIndex); return Range ((float) (info.origin.y - info.descent), (float) (info.origin.y + info.ascent)); } static float findCTFrameHeight (CTFrameRef frame) { CFArrayRef lines = CTFrameGetLines (frame); const CFIndex numLines = CFArrayGetCount (lines); if (numLines == 0) return 0; Range range (getLineVerticalRange (frame, lines, 0)); if (numLines > 1) range = range.getUnionWith (getLineVerticalRange (frame, lines, (int) numLines - 1)); return range.getLength(); } static void drawToCGContext (const AttributedString& text, const Rectangle& area, const CGContextRef& context, const float flipHeight) { CTFrameRef frame = createCTFrame (text, CGRectMake ((CGFloat) area.getX(), flipHeight - (CGFloat) area.getBottom(), (CGFloat) area.getWidth(), (CGFloat) area.getHeight())); const int verticalJustification = text.getJustification().getOnlyVerticalFlags(); if (verticalJustification == Justification::verticallyCentred || verticalJustification == Justification::bottom) { float adjust = area.getHeight() - findCTFrameHeight (frame); if (verticalJustification == Justification::verticallyCentred) adjust *= 0.5f; CGContextSaveGState (context); CGContextTranslateCTM (context, 0, -adjust); CTFrameDraw (frame, context); CGContextRestoreGState (context); } else { CTFrameDraw (frame, context); } CFRelease (frame); } static void createLayout (TextLayout& glyphLayout, const AttributedString& text) { const CGFloat boundsHeight = glyphLayout.getHeight(); CTFrameRef frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight)); CFArrayRef lines = CTFrameGetLines (frame); const CFIndex numLines = CFArrayGetCount (lines); glyphLayout.ensureStorageAllocated ((int) numLines); for (CFIndex i = 0; i < numLines; ++i) { CTLineRef line = (CTLineRef) CFArrayGetValueAtIndex (lines, i); CFArrayRef runs = CTLineGetGlyphRuns (line); const CFIndex numRuns = CFArrayGetCount (runs); const CFRange cfrlineStringRange = CTLineGetStringRange (line); const CFIndex lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length - 1; const Range lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd); LineInfo lineInfo (frame, line, i); TextLayout::Line* const glyphLine = new TextLayout::Line (lineStringRange, Point ((float) lineInfo.origin.x, (float) (boundsHeight - lineInfo.origin.y)), (float) lineInfo.ascent, (float) lineInfo.descent, (float) lineInfo.leading, (int) numRuns); glyphLayout.addLine (glyphLine); for (CFIndex j = 0; j < numRuns; ++j) { CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runs, j); const CFIndex numGlyphs = CTRunGetGlyphCount (run); const CFRange runStringRange = CTRunGetStringRange (run); TextLayout::Run* const glyphRun = new TextLayout::Run (Range ((int) runStringRange.location, (int) (runStringRange.location + runStringRange.length - 1)), (int) numGlyphs); glyphLine->runs.add (glyphRun); CFDictionaryRef runAttributes = CTRunGetAttributes (run); CTFontRef ctRunFont; if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void **) &ctRunFont)) { CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont); CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr); CFRelease (cfsFontName); const float fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef); CFRelease (ctFontRef); CFStringRef cfsFontFamily = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute); CFStringRef cfsFontStyle = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute); glyphRun->font = Font (String::fromCFString (cfsFontFamily), String::fromCFString (cfsFontStyle), (float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor)); CFRelease (cfsFontStyle); CFRelease (cfsFontFamily); } CGColorRef cgRunColor; if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor) && CGColorGetNumberOfComponents (cgRunColor) == 4) { const CGFloat* const components = CGColorGetComponents (cgRunColor); glyphRun->colour = Colour::fromFloatRGBA ((float) components[0], (float) components[1], (float) components[2], (float) components[3]); } const Glyphs glyphs (run, (size_t) numGlyphs); const Advances advances (run, numGlyphs); const Positions positions (run, (size_t) numGlyphs); for (CFIndex k = 0; k < numGlyphs; ++k) glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point ((float) positions.points[k].x, (float) positions.points[k].y), (float) advances.advances[k].width)); } } CFRelease (frame); } } //============================================================================== class OSXTypeface : public Typeface { public: OSXTypeface (const Font& font) : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), fontRef (nullptr), ctFontRef (nullptr), fontHeightToPointsFactor (1.0f), renderingTransform (CGAffineTransformIdentity), isMemoryFont (false), attributedStringAtts (nullptr), ascent (0.0f), unitsToHeightScaleFactor (0.0f) { ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform); if (ctFontRef != nullptr) { fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); initialiseMetrics(); } } OSXTypeface (const void* data, size_t dataSize) : Typeface (String(), String()), fontRef (nullptr), ctFontRef (nullptr), fontHeightToPointsFactor (1.0f), renderingTransform (CGAffineTransformIdentity), isMemoryFont (true), attributedStringAtts (nullptr), ascent (0.0f), unitsToHeightScaleFactor (0.0f) { CFDataRef cfData = CFDataCreate (kCFAllocatorDefault, (const UInt8*) data, (CFIndex) dataSize); CGDataProviderRef provider = CGDataProviderCreateWithCFData (cfData); CFRelease (cfData); fontRef = CGFontCreateWithDataProvider (provider); CGDataProviderRelease (provider); if (fontRef != nullptr) { ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr); if (ctFontRef != nullptr) { if (CFStringRef fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey)) { name = String::fromCFString (fontName); CFRelease (fontName); } if (CFStringRef fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey)) { style = String::fromCFString (fontStyle); CFRelease (fontStyle); } initialiseMetrics(); } } } void initialiseMetrics() { const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef)); const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef)); const float ctTotalHeight = ctAscent + ctDescent; ascent = ctAscent / ctTotalHeight; unitsToHeightScaleFactor = 1.0f / ctTotalHeight; pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); fontHeightToPointsFactor = referenceFontSize / ctTotalHeight; const short zero = 0; CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero); CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName }; CFTypeRef values[] = { ctFontRef, numberRef }; attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease (numberRef); } ~OSXTypeface() { if (attributedStringAtts != nullptr) CFRelease (attributedStringAtts); if (fontRef != nullptr) CGFontRelease (fontRef); if (ctFontRef != nullptr) CFRelease (ctFontRef); } float getAscent() const override { return ascent; } float getDescent() const override { return 1.0f - ascent; } float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; } float getStringWidth (const String& text) override { float x = 0; if (ctFontRef != nullptr && text.isNotEmpty()) { CFStringRef cfText = text.toCFString(); CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts); CFRelease (cfText); CTLineRef line = CTLineCreateWithAttributedString (attribString); CFArrayRef runArray = CTLineGetGlyphRuns (line); for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i) { CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i); CFIndex length = CTRunGetGlyphCount (run); const CoreTextTypeLayout::Advances advances (run, length); for (int j = 0; j < length; ++j) x += (float) advances.advances[j].width; } CFRelease (line); CFRelease (attribString); x *= unitsToHeightScaleFactor; } return x; } void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) override { xOffsets.add (0); if (ctFontRef != nullptr && text.isNotEmpty()) { float x = 0; CFStringRef cfText = text.toCFString(); CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts); CFRelease (cfText); CTLineRef line = CTLineCreateWithAttributedString (attribString); CFArrayRef runArray = CTLineGetGlyphRuns (line); for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i) { CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i); CFIndex length = CTRunGetGlyphCount (run); const CoreTextTypeLayout::Advances advances (run, length); const CoreTextTypeLayout::Glyphs glyphs (run, (size_t) length); for (int j = 0; j < length; ++j) { x += (float) advances.advances[j].width; xOffsets.add (x * unitsToHeightScaleFactor); resultGlyphs.add (glyphs.glyphs[j]); } } CFRelease (line); CFRelease (attribString); } } bool getOutlineForGlyph (int glyphNumber, Path& path) override { jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty CGPathRef pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform); if (pathRef == 0) return false; CGPathApply (pathRef, &path, pathApplier); CFRelease (pathRef); if (! pathTransform.isIdentity()) path.applyTransform (pathTransform); return true; } //============================================================================== CGFontRef fontRef; CTFontRef ctFontRef; float fontHeightToPointsFactor; CGAffineTransform renderingTransform; bool isMemoryFont; private: CFDictionaryRef attributedStringAtts; float ascent, unitsToHeightScaleFactor; AffineTransform pathTransform; static void pathApplier (void* info, const CGPathElement* const element) { Path& path = *static_cast (info); const CGPoint* const p = element->points; switch (element->type) { case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break; case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break; case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y, (float) p[1].x, (float) -p[1].y); break; case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y, (float) p[1].x, (float) -p[1].y, (float) p[2].x, (float) -p[2].y); break; case kCGPathElementCloseSubpath: path.closeSubPath(); break; default: jassertfalse; break; } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface) }; CTFontRef getCTFontFromTypeface (const Font& f) { if (OSXTypeface* tf = dynamic_cast (f.getTypeface())) return tf->ctFontRef; return 0; } StringArray Font::findAllTypefaceNames() { StringArray names; #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 && ! JUCE_IOS // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS CFArrayRef fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames(); for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i) { const String family (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i))); if (! family.startsWithChar ('.')) // ignore fonts that start with a '.' names.addIfNotAlreadyThere (family); } CFRelease (fontFamilyArray); #else CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr); CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef); CFRelease (fontCollectionRef); for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i) { CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i); CFStringRef cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute); names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily)); CFRelease (cfsFontFamily); } CFRelease (fontDescriptorArray); #endif names.sort (true); return names; } StringArray Font::findAllTypefaceStyles (const String& family) { if (FontStyleHelpers::isPlaceholderFamilyName (family)) return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); StringArray results; CFStringRef cfsFontFamily = family.toCFString(); CFStringRef keys[] = { kCTFontFamilyNameAttribute }; CFTypeRef values[] = { cfsFontFamily }; CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease (cfsFontFamily); CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes); CFRelease (fontDescAttributes); CFArrayRef fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks); CFRelease (ctFontDescRef); CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr); CFRelease (fontFamilyArray); CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef); CFRelease (fontCollectionRef); if (fontDescriptorArray != nullptr) { for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i) { CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i); CFStringRef cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute); results.add (String::fromCFString (cfsFontStyle)); CFRelease (cfsFontStyle); } CFRelease (fontDescriptorArray); } return results; } #else //============================================================================== // The stuff that follows is a mash-up that supports pre-OSX 10.5 APIs. // (Hopefully all of this can be ditched at some point in the future). //============================================================================== #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 #define SUPPORT_10_4_FONTS 1 #define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0) #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 #define SUPPORT_ONLY_10_4_FONTS 1 #endif } // (juce namespace) @interface NSFont (PrivateHack) - (NSGlyph) _defaultGlyphForChar: (unichar) theChar; @end namespace juce { #endif //============================================================================== class OSXTypeface : public Typeface { public: OSXTypeface (const Font& font) : Typeface (font.getTypefaceName(), font.getTypefaceStyle()) { JUCE_AUTORELEASEPOOL { renderingTransform = CGAffineTransformIdentity; NSDictionary* nsDict = [NSDictionary dictionaryWithObjectsAndKeys: juceStringToNS (name), NSFontFamilyAttribute, juceStringToNS (style), NSFontFaceAttribute, nil]; NSFontDescriptor* nsFontDesc = [NSFontDescriptor fontDescriptorWithFontAttributes: nsDict]; nsFont = [NSFont fontWithDescriptor: nsFontDesc size: referenceFontSize]; [nsFont retain]; #if SUPPORT_ONLY_10_4_FONTS initWithATSFont(); #else #if SUPPORT_10_4_FONTS if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) { initWithATSFont(); } else #endif { fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); const float absAscent = std::abs ((float) CGFontGetAscent (fontRef)); const float totalHeight = absAscent + std::abs ((float) CGFontGetDescent (fontRef)); ascent = absAscent / totalHeight; unitsToHeightScaleFactor = 1.0f / totalHeight; const float nsFontAscent = std::abs ([nsFont ascender]); const float nsFontDescent = std::abs ([nsFont descender]); fontHeightToPointsFactor = referenceFontSize / (nsFontAscent + nsFontDescent); } #endif pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); } } ~OSXTypeface() { #if ! JUCE_IOS [nsFont release]; #endif if (fontRef != 0) CGFontRelease (fontRef); } #if SUPPORT_10_4_FONTS void initWithATSFont() { ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); if (atsFont == 0) atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); fontRef = CGFontCreateWithPlatformFont (&atsFont); const float absAscent = std::abs ([nsFont ascender]); const float absDescent = std::abs ([nsFont descender]); const float totalHeight = absAscent + absDescent; unitsToHeightScaleFactor = 1.0f / totalHeight; fontHeightToPointsFactor = referenceFontSize / totalHeight; ascent = absAscent / totalHeight; } #endif float getAscent() const override { return ascent; } float getDescent() const override { return 1.0f - ascent; } float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; } float getStringWidth (const String& text) override { if (fontRef == 0 || text.isEmpty()) return 0; const int length = text.length(); HeapBlock glyphs; createGlyphsForString (text.getCharPointer(), length, glyphs); float x = 0; #if SUPPORT_ONLY_10_4_FONTS HeapBlock advances (length); [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; for (int i = 0; i < length; ++i) x += advances[i].width; #else #if SUPPORT_10_4_FONTS if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) { HeapBlock advances (length); [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; for (int i = 0; i < length; ++i) x += advances[i].width; } else #endif { HeapBlock advances (length); if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) for (int i = 0; i < length; ++i) x += advances[i]; } #endif return x * unitsToHeightScaleFactor; } void getGlyphPositions (const String& text, Array& resultGlyphs, Array& xOffsets) override { xOffsets.add (0); if (fontRef == 0 || text.isEmpty()) return; const int length = text.length(); HeapBlock glyphs; createGlyphsForString (text.getCharPointer(), length, glyphs); #if SUPPORT_ONLY_10_4_FONTS HeapBlock advances (length); [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; int x = 0; for (int i = 0; i < length; ++i) { x += advances[i].width; xOffsets.add (x * unitsToHeightScaleFactor); resultGlyphs.add (reinterpret_cast (glyphs.getData())[i]); } #else #if SUPPORT_10_4_FONTS if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) { HeapBlock advances (length); NSGlyph* const nsGlyphs = reinterpret_cast (glyphs.getData()); [nsFont getAdvancements: advances forGlyphs: nsGlyphs count: length]; float x = 0; for (int i = 0; i < length; ++i) { x += advances[i].width; xOffsets.add (x * unitsToHeightScaleFactor); resultGlyphs.add (nsGlyphs[i]); } } else #endif { HeapBlock advances (length); if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) { int x = 0; for (int i = 0; i < length; ++i) { x += advances [i]; xOffsets.add (x * unitsToHeightScaleFactor); resultGlyphs.add (glyphs[i]); } } } #endif } bool getOutlineForGlyph (int glyphNumber, Path& path) override { #if JUCE_IOS return false; #else if (nsFont == nil) return false; // we might need to apply a transform to the path, so it mustn't have anything else in it jassert (path.isEmpty()); JUCE_AUTORELEASEPOOL { NSBezierPath* bez = [NSBezierPath bezierPath]; [bez moveToPoint: NSMakePoint (0, 0)]; [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber inFont: nsFont]; for (int i = 0; i < [bez elementCount]; ++i) { NSPoint p[3]; switch ([bez elementAtIndex: i associatedPoints: p]) { case NSMoveToBezierPathElement: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break; case NSLineToBezierPathElement: path.lineTo ((float) p[0].x, (float) -p[0].y); break; case NSCurveToBezierPathElement: path.cubicTo ((float) p[0].x, (float) -p[0].y, (float) p[1].x, (float) -p[1].y, (float) p[2].x, (float) -p[2].y); break; case NSClosePathBezierPathElement: path.closeSubPath(); break; default: jassertfalse; break; } } path.applyTransform (pathTransform); } return true; #endif } //============================================================================== CGFontRef fontRef; float fontHeightToPointsFactor; CGAffineTransform renderingTransform; private: float ascent, unitsToHeightScaleFactor; #if ! JUCE_IOS NSFont* nsFont; AffineTransform pathTransform; #endif void createGlyphsForString (String::CharPointerType text, const int length, HeapBlock & glyphs) { #if SUPPORT_10_4_FONTS #if ! SUPPORT_ONLY_10_4_FONTS if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) #endif { glyphs.malloc (sizeof (NSGlyph) * length, 1); NSGlyph* const nsGlyphs = reinterpret_cast (glyphs.getData()); for (int i = 0; i < length; ++i) nsGlyphs[i] = (NSGlyph) [nsFont _defaultGlyphForChar: text.getAndAdvance()]; return; } #endif #if ! SUPPORT_ONLY_10_4_FONTS if (charToGlyphMapper == nullptr) charToGlyphMapper = new CharToGlyphMapper (fontRef); glyphs.malloc (length); for (int i = 0; i < length; ++i) glyphs[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text.getAndAdvance()); #endif } #if ! SUPPORT_ONLY_10_4_FONTS // Reads a CGFontRef's character map table to convert unicode into glyph numbers class CharToGlyphMapper { public: CharToGlyphMapper (CGFontRef cgFontRef) : segCount (0), endCode (0), startCode (0), idDelta (0), idRangeOffset (0), glyphIndexes (0) { CFDataRef cmapTable = CGFontCopyTableForTag (cgFontRef, 'cmap'); if (cmapTable != 0) { const int numSubtables = getValue16 (cmapTable, 2); for (int i = 0; i < numSubtables; ++i) { if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0 { const int offset = getValue32 (cmapTable, i * 8 + 8); if (getValue16 (cmapTable, offset) == 4) // check that it's format 4.. { const int length = getValue16 (cmapTable, offset + 2); const int segCountX2 = getValue16 (cmapTable, offset + 6); segCount = segCountX2 / 2; const int endCodeOffset = offset + 14; const int startCodeOffset = endCodeOffset + 2 + segCountX2; const int idDeltaOffset = startCodeOffset + segCountX2; const int idRangeOffsetOffset = idDeltaOffset + segCountX2; const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2; endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2); startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2); idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2); idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2); glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset); } break; } } CFRelease (cmapTable); } } ~CharToGlyphMapper() { if (endCode != 0) { CFRelease (endCode); CFRelease (startCode); CFRelease (idDelta); CFRelease (idRangeOffset); CFRelease (glyphIndexes); } } int getGlyphForCharacter (const juce_wchar c) const { for (int i = 0; i < segCount; ++i) { if (getValue16 (endCode, i * 2) >= c) { const int start = getValue16 (startCode, i * 2); if (start > c) break; const int delta = getValue16 (idDelta, i * 2); const int rangeOffset = getValue16 (idRangeOffset, i * 2); if (rangeOffset == 0) return delta + c; return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i))); } } // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts! return jmax (-1, (int) c - 29); } private: int segCount; CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes; static uint16 getValue16 (CFDataRef data, const int index) { return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index)); } static uint32 getValue32 (CFDataRef data, const int index) { return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index)); } }; ScopedPointer charToGlyphMapper; #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface) }; StringArray Font::findAllTypefaceNames() { StringArray names; JUCE_AUTORELEASEPOOL { #if JUCE_IOS for (NSString* name in [UIFont familyNames]) #else for (NSString* name in [[NSFontManager sharedFontManager] availableFontFamilies]) #endif names.add (nsStringToJuce (name)); names.sort (true); } return names; } StringArray Font::findAllTypefaceStyles (const String& family) { if (FontStyleHelpers::isPlaceholderFamilyName (family)) return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); StringArray results; JUCE_AUTORELEASEPOOL { for (NSArray* style in [[NSFontManager sharedFontManager] availableMembersOfFontFamily: juceStringToNS (family)]) results.add (nsStringToJuce ((NSString*) [style objectAtIndex: 1])); } return results; } #endif //============================================================================== Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return new OSXTypeface (font); } Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) { #if JUCE_CORETEXT_AVAILABLE return new OSXTypeface (data, dataSize); #else jassertfalse; // You need CoreText enabled to use this feature! return nullptr; #endif } void Typeface::scanFolderForFonts (const File&) { jassertfalse; // not implemented on this platform } struct DefaultFontNames { DefaultFontNames() #if JUCE_IOS : defaultSans ("Helvetica"), defaultSerif ("Times New Roman"), defaultFixed ("Courier New"), #else : defaultSans ("Lucida Grande"), defaultSerif ("Times New Roman"), defaultFixed ("Menlo"), #endif defaultFallback ("Arial Unicode MS") { } String defaultSans, defaultSerif, defaultFixed, defaultFallback; }; Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; Font newFont (font); const String& faceName = font.getTypefaceName(); if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed); if (font.getTypefaceStyle() == getDefaultStyle()) newFont.setTypefaceStyle ("Regular"); return Typeface::createSystemTypefaceFor (newFont); } #if JUCE_CORETEXT_AVAILABLE static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) { const int numCharacterAttributes = text.getNumAttributes(); for (int i = 0; i < numCharacterAttributes; ++i) { if (const Font* const f = text.getAttribute (i)->getFont()) { if (OSXTypeface* tf = dynamic_cast (f->getTypeface())) { if (tf->isMemoryFont) return false; } else if (dynamic_cast (f->getTypeface()) != nullptr) { return false; } } } return true; } #endif bool TextLayout::createNativeLayout (const AttributedString& text) { #if JUCE_CORETEXT_AVAILABLE // Seems to be an unfathomable bug in CoreText which prevents the layout working with // typefaces that were loaded from memory, so have to fallback if we hit any of those.. if (canAllTypefacesBeUsedInLayout (text)) { CoreTextTypeLayout::createLayout (*this, text); return true; } #endif (void) text; return false; } juce_win32_Direct2DGraphicsContext.cpp000066400000000000000000000721511320201440200350110ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class Direct2DLowLevelGraphicsContext : public LowLevelGraphicsContext { public: Direct2DLowLevelGraphicsContext (HWND hwnd_) : hwnd (hwnd_), currentState (nullptr) { RECT windowRect; GetClientRect (hwnd, &windowRect); D2D1_SIZE_U size = { windowRect.right - windowRect.left, windowRect.bottom - windowRect.top }; bounds.setSize (size.width, size.height); D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(); D2D1_HWND_RENDER_TARGET_PROPERTIES propsHwnd = D2D1::HwndRenderTargetProperties (hwnd, size); if (factories->d2dFactory != nullptr) { HRESULT hr = factories->d2dFactory->CreateHwndRenderTarget (props, propsHwnd, renderingTarget.resetAndGetPointerAddress()); jassert (SUCCEEDED (hr)); (void) hr; hr = renderingTarget->CreateSolidColorBrush (D2D1::ColorF::ColorF (0.0f, 0.0f, 0.0f, 1.0f), colourBrush.resetAndGetPointerAddress()); } } ~Direct2DLowLevelGraphicsContext() { states.clear(); } void resized() { RECT windowRect; GetClientRect (hwnd, &windowRect); D2D1_SIZE_U size = { windowRect.right - windowRect.left, windowRect.bottom - windowRect.top }; renderingTarget->Resize (size); bounds.setSize (size.width, size.height); } void clear() { renderingTarget->Clear (D2D1::ColorF (D2D1::ColorF::White, 0.0f)); // xxx why white and not black? } void start() { renderingTarget->BeginDraw(); saveState(); } void end() { states.clear(); currentState = 0; renderingTarget->EndDraw(); renderingTarget->CheckWindowState(); } bool isVectorDevice() const { return false; } void setOrigin (Point o) { addTransform (AffineTransform::translation ((float) o.x, (float) o.y)); } void addTransform (const AffineTransform& transform) { currentState->transform = transform.followedBy (currentState->transform); } float getPhysicalPixelScaleFactor() { return currentState->transform.getScaleFactor(); } bool clipToRectangle (const Rectangle& r) { currentState->clipToRectangle (r); return ! isClipEmpty(); } bool clipToRectangleList (const RectangleList& clipRegion) { currentState->clipToRectList (rectListToPathGeometry (clipRegion)); return ! isClipEmpty(); } void excludeClipRectangle (const Rectangle&) { //xxx } void clipToPath (const Path& path, const AffineTransform& transform) { currentState->clipToPath (pathToPathGeometry (path, transform)); } void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) { currentState->clipToImage (sourceImage, transform); } bool clipRegionIntersects (const Rectangle& r) { return currentState->clipRect.intersects (r.toFloat().transformed (currentState->transform).getSmallestIntegerContainer()); } Rectangle getClipBounds() const { // xxx could this take into account complex clip regions? return currentState->clipRect.toFloat().transformed (currentState->transform.inverted()).getSmallestIntegerContainer(); } bool isClipEmpty() const { return currentState->clipRect.isEmpty(); } void saveState() { states.add (new SavedState (*this)); currentState = states.getLast(); } void restoreState() { jassert (states.size() > 1) //you should never pop the last state! states.removeLast (1); currentState = states.getLast(); } void beginTransparencyLayer (float /*opacity*/) { jassertfalse; //xxx todo } void endTransparencyLayer() { jassertfalse; //xxx todo } void setFill (const FillType& fillType) { currentState->setFill (fillType); } void setOpacity (float newOpacity) { currentState->setOpacity (newOpacity); } void setInterpolationQuality (Graphics::ResamplingQuality /*quality*/) { } void fillRect (const Rectangle& r, bool /*replaceExistingContents*/) { fillRect (r.toFloat()); } void fillRect (const Rectangle& r) { renderingTarget->SetTransform (transformToMatrix (currentState->transform)); currentState->createBrush(); renderingTarget->FillRectangle (rectangleToRectF (r), currentState->currentBrush); renderingTarget->SetTransform (D2D1::IdentityMatrix()); } void fillRectList (const RectangleList& list) { for (const Rectangle* r = list.begin(), * const e = list.end(); r != e; ++r) fillRect (*r); } void fillPath (const Path& p, const AffineTransform& transform) { currentState->createBrush(); ComSmartPtr geometry (pathToPathGeometry (p, transform.followedBy (currentState->transform))); if (renderingTarget != nullptr) renderingTarget->FillGeometry (geometry, currentState->currentBrush); } void drawImage (const Image& image, const AffineTransform& transform) { renderingTarget->SetTransform (transformToMatrix (transform.followedBy (currentState->transform))); D2D1_SIZE_U size; size.width = image.getWidth(); size.height = image.getHeight(); D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties(); Image img (image.convertedToFormat (Image::ARGB)); Image::BitmapData bd (img, Image::BitmapData::readOnly); bp.pixelFormat = renderingTarget->GetPixelFormat(); bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; { ComSmartPtr tempBitmap; renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, tempBitmap.resetAndGetPointerAddress()); if (tempBitmap != nullptr) renderingTarget->DrawBitmap (tempBitmap); } renderingTarget->SetTransform (D2D1::IdentityMatrix()); } void drawLine (const Line & line) { // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour renderingTarget->SetTransform (transformToMatrix (currentState->transform)); currentState->createBrush(); renderingTarget->DrawLine (D2D1::Point2F (line.getStartX(), line.getStartY()), D2D1::Point2F (line.getEndX(), line.getEndY()), currentState->currentBrush); renderingTarget->SetTransform (D2D1::IdentityMatrix()); } void setFont (const Font& newFont) { currentState->setFont (newFont); } const Font& getFont() { return currentState->font; } void drawGlyph (int glyphNumber, const AffineTransform& transform) { currentState->createBrush(); currentState->createFont(); float hScale = currentState->font.getHorizontalScale(); renderingTarget->SetTransform (transformToMatrix (AffineTransform::scale (hScale, 1.0f) .followedBy (transform) .followedBy (currentState->transform))); const UINT16 glyphIndices = (UINT16) glyphNumber; const FLOAT glyphAdvances = 0; DWRITE_GLYPH_OFFSET offset; offset.advanceOffset = 0; offset.ascenderOffset = 0; DWRITE_GLYPH_RUN glyphRun; glyphRun.fontFace = currentState->currentFontFace; glyphRun.fontEmSize = (FLOAT) (currentState->font.getHeight() * currentState->fontHeightToEmSizeFactor); glyphRun.glyphCount = 1; glyphRun.glyphIndices = &glyphIndices; glyphRun.glyphAdvances = &glyphAdvances; glyphRun.glyphOffsets = &offset; glyphRun.isSideways = FALSE; glyphRun.bidiLevel = 0; renderingTarget->DrawGlyphRun (D2D1::Point2F (0, 0), &glyphRun, currentState->currentBrush); renderingTarget->SetTransform (D2D1::IdentityMatrix()); } bool drawTextLayout (const AttributedString& text, const Rectangle& area) { renderingTarget->SetTransform (transformToMatrix (currentState->transform)); DirectWriteTypeLayout::drawToD2DContext (text, area, renderingTarget, factories->directWriteFactory, factories->d2dFactory, factories->systemFonts); renderingTarget->SetTransform (D2D1::IdentityMatrix()); return true; } //============================================================================== class SavedState { public: SavedState (Direct2DLowLevelGraphicsContext& owner_) : owner (owner_), currentBrush (0), fontHeightToEmSizeFactor (1.0f), currentFontFace (0), clipsRect (false), shouldClipRect (false), clipsRectList (false), shouldClipRectList (false), clipsComplex (false), shouldClipComplex (false), clipsBitmap (false), shouldClipBitmap (false) { if (owner.currentState != nullptr) { // xxx seems like a very slow way to create one of these, and this is a performance // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write? setFill (owner.currentState->fillType); currentBrush = owner.currentState->currentBrush; clipRect = owner.currentState->clipRect; transform = owner.currentState->transform; font = owner.currentState->font; currentFontFace = owner.currentState->currentFontFace; } else { const D2D1_SIZE_U size (owner.renderingTarget->GetPixelSize()); clipRect.setSize (size.width, size.height); setFill (FillType (Colours::black)); } } ~SavedState() { clearClip(); clearFont(); clearFill(); clearPathClip(); clearImageClip(); complexClipLayer = 0; bitmapMaskLayer = 0; } void clearClip() { popClips(); shouldClipRect = false; } void clipToRectangle (const Rectangle& r) { clearClip(); clipRect = r.toFloat().transformed (transform).getSmallestIntegerContainer(); shouldClipRect = true; pushClips(); } void clearPathClip() { popClips(); if (shouldClipComplex) { complexClipGeometry = 0; shouldClipComplex = false; } } void clipToPath (ID2D1Geometry* geometry) { clearPathClip(); if (complexClipLayer == 0) owner.renderingTarget->CreateLayer (complexClipLayer.resetAndGetPointerAddress()); complexClipGeometry = geometry; shouldClipComplex = true; pushClips(); } void clearRectListClip() { popClips(); if (shouldClipRectList) { rectListGeometry = 0; shouldClipRectList = false; } } void clipToRectList (ID2D1Geometry* geometry) { clearRectListClip(); if (rectListLayer == 0) owner.renderingTarget->CreateLayer (rectListLayer.resetAndGetPointerAddress()); rectListGeometry = geometry; shouldClipRectList = true; pushClips(); } void clearImageClip() { popClips(); if (shouldClipBitmap) { maskBitmap = 0; bitmapMaskBrush = 0; shouldClipBitmap = false; } } void clipToImage (const Image& image, const AffineTransform& transform) { clearImageClip(); if (bitmapMaskLayer == 0) owner.renderingTarget->CreateLayer (bitmapMaskLayer.resetAndGetPointerAddress()); D2D1_BRUSH_PROPERTIES brushProps; brushProps.opacity = 1; brushProps.transform = transformToMatrix (transform); D2D1_BITMAP_BRUSH_PROPERTIES bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP); D2D1_SIZE_U size; size.width = image.getWidth(); size.height = image.getHeight(); D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties(); maskImage = image.convertedToFormat (Image::ARGB); Image::BitmapData bd (this->image, Image::BitmapData::readOnly); // xxx should be maskImage? bp.pixelFormat = owner.renderingTarget->GetPixelFormat(); bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; HRESULT hr = owner.renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, maskBitmap.resetAndGetPointerAddress()); hr = owner.renderingTarget->CreateBitmapBrush (maskBitmap, bmProps, brushProps, bitmapMaskBrush.resetAndGetPointerAddress()); imageMaskLayerParams = D2D1::LayerParameters(); imageMaskLayerParams.opacityBrush = bitmapMaskBrush; shouldClipBitmap = true; pushClips(); } void popClips() { if (clipsBitmap) { owner.renderingTarget->PopLayer(); clipsBitmap = false; } if (clipsComplex) { owner.renderingTarget->PopLayer(); clipsComplex = false; } if (clipsRectList) { owner.renderingTarget->PopLayer(); clipsRectList = false; } if (clipsRect) { owner.renderingTarget->PopAxisAlignedClip(); clipsRect = false; } } void pushClips() { if (shouldClipRect && ! clipsRect) { owner.renderingTarget->PushAxisAlignedClip (rectangleToRectF (clipRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); clipsRect = true; } if (shouldClipRectList && ! clipsRectList) { D2D1_LAYER_PARAMETERS layerParams = D2D1::LayerParameters(); rectListGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds); layerParams.geometricMask = rectListGeometry; owner.renderingTarget->PushLayer (layerParams, rectListLayer); clipsRectList = true; } if (shouldClipComplex && ! clipsComplex) { D2D1_LAYER_PARAMETERS layerParams = D2D1::LayerParameters(); complexClipGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds); layerParams.geometricMask = complexClipGeometry; owner.renderingTarget->PushLayer (layerParams, complexClipLayer); clipsComplex = true; } if (shouldClipBitmap && ! clipsBitmap) { owner.renderingTarget->PushLayer (imageMaskLayerParams, bitmapMaskLayer); clipsBitmap = true; } } void setFill (const FillType& newFillType) { if (fillType != newFillType) { fillType = newFillType; clearFill(); } } void clearFont() { currentFontFace = localFontFace = 0; } void setFont (const Font& newFont) { if (font != newFont) { font = newFont; clearFont(); } } void createFont() { if (currentFontFace == nullptr) { WindowsDirectWriteTypeface* typeface = dynamic_cast (font.getTypeface()); currentFontFace = typeface->getIDWriteFontFace(); fontHeightToEmSizeFactor = typeface->unitsToHeightScaleFactor(); } } void setOpacity (float newOpacity) { fillType.setOpacity (newOpacity); if (currentBrush != nullptr) currentBrush->SetOpacity (newOpacity); } void clearFill() { gradientStops = 0; linearGradient = 0; radialGradient = 0; bitmap = 0; bitmapBrush = 0; currentBrush = 0; } void createBrush() { if (currentBrush == 0) { if (fillType.isColour()) { D2D1_COLOR_F colour = colourToD2D (fillType.colour); owner.colourBrush->SetColor (colour); currentBrush = owner.colourBrush; } else if (fillType.isTiledImage()) { D2D1_BRUSH_PROPERTIES brushProps; brushProps.opacity = fillType.getOpacity(); brushProps.transform = transformToMatrix (fillType.transform); D2D1_BITMAP_BRUSH_PROPERTIES bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP,D2D1_EXTEND_MODE_WRAP); image = fillType.image; D2D1_SIZE_U size; size.width = image.getWidth(); size.height = image.getHeight(); D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties(); this->image = image.convertedToFormat (Image::ARGB); Image::BitmapData bd (this->image, Image::BitmapData::readOnly); bp.pixelFormat = owner.renderingTarget->GetPixelFormat(); bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; HRESULT hr = owner.renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, bitmap.resetAndGetPointerAddress()); hr = owner.renderingTarget->CreateBitmapBrush (bitmap, bmProps, brushProps, bitmapBrush.resetAndGetPointerAddress()); currentBrush = bitmapBrush; } else if (fillType.isGradient()) { gradientStops = 0; D2D1_BRUSH_PROPERTIES brushProps; brushProps.opacity = fillType.getOpacity(); brushProps.transform = transformToMatrix (fillType.transform.followedBy (transform)); const int numColors = fillType.gradient->getNumColours(); HeapBlock stops (numColors); for (int i = fillType.gradient->getNumColours(); --i >= 0;) { stops[i].color = colourToD2D (fillType.gradient->getColour(i)); stops[i].position = (FLOAT) fillType.gradient->getColourPosition(i); } owner.renderingTarget->CreateGradientStopCollection (stops.getData(), numColors, gradientStops.resetAndGetPointerAddress()); if (fillType.gradient->isRadial) { radialGradient = 0; const Point p1 = fillType.gradient->point1; const Point p2 = fillType.gradient->point2; float r = p1.getDistanceFrom (p2); D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES props = D2D1::RadialGradientBrushProperties (D2D1::Point2F (p1.x, p1.y), D2D1::Point2F (0, 0), r, r); owner.renderingTarget->CreateRadialGradientBrush (props, brushProps, gradientStops, radialGradient.resetAndGetPointerAddress()); currentBrush = radialGradient; } else { linearGradient = 0; const Point p1 = fillType.gradient->point1; const Point p2 = fillType.gradient->point2; D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props = D2D1::LinearGradientBrushProperties (D2D1::Point2F (p1.x, p1.y), D2D1::Point2F (p2.x, p2.y)); owner.renderingTarget->CreateLinearGradientBrush (props, brushProps, gradientStops, linearGradient.resetAndGetPointerAddress()); currentBrush = linearGradient; } } } } //============================================================================== //xxx most of these members should probably be private... Direct2DLowLevelGraphicsContext& owner; AffineTransform transform; Font font; float fontHeightToEmSizeFactor; IDWriteFontFace* currentFontFace; ComSmartPtr localFontFace; FillType fillType; Image image; ComSmartPtr bitmap; // xxx needs a better name - what is this for?? Rectangle clipRect; bool clipsRect, shouldClipRect; ComSmartPtr complexClipGeometry; D2D1_LAYER_PARAMETERS complexClipLayerParams; ComSmartPtr complexClipLayer; bool clipsComplex, shouldClipComplex; ComSmartPtr rectListGeometry; D2D1_LAYER_PARAMETERS rectListLayerParams; ComSmartPtr rectListLayer; bool clipsRectList, shouldClipRectList; Image maskImage; D2D1_LAYER_PARAMETERS imageMaskLayerParams; ComSmartPtr bitmapMaskLayer; ComSmartPtr maskBitmap; ComSmartPtr bitmapMaskBrush; bool clipsBitmap, shouldClipBitmap; ID2D1Brush* currentBrush; ComSmartPtr bitmapBrush; ComSmartPtr linearGradient; ComSmartPtr radialGradient; ComSmartPtr gradientStops; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState) }; //============================================================================== private: SharedResourcePointer factories; HWND hwnd; ComSmartPtr renderingTarget; ComSmartPtr colourBrush; Rectangle bounds; SavedState* currentState; OwnedArray states; //============================================================================== template static D2D1_RECT_F rectangleToRectF (const Rectangle& r) { return D2D1::RectF ((float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom()); } static D2D1_COLOR_F colourToD2D (Colour c) { return D2D1::ColorF::ColorF (c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha()); } static D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform) { transform.transformPoint (x, y); return D2D1::Point2F ((FLOAT) x, (FLOAT) y); } static void rectToGeometrySink (const Rectangle& rect, ID2D1GeometrySink* sink) { sink->BeginFigure (pointTransformed (rect.getX(), rect.getY()), D2D1_FIGURE_BEGIN_FILLED); sink->AddLine (pointTransformed (rect.getRight(), rect.getY())); sink->AddLine (pointTransformed (rect.getRight(), rect.getBottom())); sink->AddLine (pointTransformed (rect.getX(), rect.getBottom())); sink->EndFigure (D2D1_FIGURE_END_CLOSED); } static ID2D1PathGeometry* rectListToPathGeometry (const RectangleList& clipRegion) { ID2D1PathGeometry* p = nullptr; factories->d2dFactory->CreatePathGeometry (&p); ComSmartPtr sink; HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); // xxx handle error sink->SetFillMode (D2D1_FILL_MODE_WINDING); for (int i = clipRegion.getNumRectangles(); --i >= 0;) rectToGeometrySink (clipRegion.getRectangle(i), sink); hr = sink->Close(); return p; } static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform) { Path::Iterator it (path); while (it.next()) { switch (it.elementType) { case Path::Iterator::cubicTo: { D2D1_BEZIER_SEGMENT seg; transform.transformPoint (it.x1, it.y1); seg.point1 = D2D1::Point2F (it.x1, it.y1); transform.transformPoint (it.x2, it.y2); seg.point2 = D2D1::Point2F (it.x2, it.y2); transform.transformPoint(it.x3, it.y3); seg.point3 = D2D1::Point2F (it.x3, it.y3); sink->AddBezier (seg); break; } case Path::Iterator::lineTo: { transform.transformPoint (it.x1, it.y1); sink->AddLine (D2D1::Point2F (it.x1, it.y1)); break; } case Path::Iterator::quadraticTo: { D2D1_QUADRATIC_BEZIER_SEGMENT seg; transform.transformPoint (it.x1, it.y1); seg.point1 = D2D1::Point2F (it.x1, it.y1); transform.transformPoint (it.x2, it.y2); seg.point2 = D2D1::Point2F (it.x2, it.y2); sink->AddQuadraticBezier (seg); break; } case Path::Iterator::closePath: { sink->EndFigure (D2D1_FIGURE_END_CLOSED); break; } case Path::Iterator::startNewSubPath: { transform.transformPoint (it.x1, it.y1); sink->BeginFigure (D2D1::Point2F (it.x1, it.y1), D2D1_FIGURE_BEGIN_FILLED); break; } } } } static ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform) { ID2D1PathGeometry* p = nullptr; factories->d2dFactory->CreatePathGeometry (&p); ComSmartPtr sink; HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding() pathToGeometrySink (path, sink, transform); hr = sink->Close(); return p; } static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform) { D2D1::Matrix3x2F matrix; matrix._11 = transform.mat00; matrix._12 = transform.mat10; matrix._21 = transform.mat01; matrix._22 = transform.mat11; matrix._31 = transform.mat02; matrix._32 = transform.mat12; return matrix; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DLowLevelGraphicsContext) }; juce_win32_DirectWriteTypeLayout.cpp000066400000000000000000000514731320201440200346540ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ //================================================================================================== #if JUCE_USE_DIRECTWRITE namespace DirectWriteTypeLayout { class CustomDirectWriteTextRenderer : public ComBaseClassHelper { public: CustomDirectWriteTextRenderer (IDWriteFontCollection* const fonts, const AttributedString& as) : ComBaseClassHelper (0), attributedString (as), fontCollection (fonts), currentLine (-1), lastOriginY (-10000.0f) { } JUCE_COMRESULT QueryInterface (REFIID refId, void** result) { if (refId == __uuidof (IDWritePixelSnapping)) return castToType (result); return ComBaseClassHelper::QueryInterface (refId, result); } JUCE_COMRESULT IsPixelSnappingDisabled (void* /*clientDrawingContext*/, BOOL* isDisabled) { *isDisabled = FALSE; return S_OK; } JUCE_COMRESULT GetCurrentTransform (void*, DWRITE_MATRIX*) { return S_OK; } JUCE_COMRESULT GetPixelsPerDip (void*, FLOAT*) { return S_OK; } JUCE_COMRESULT DrawUnderline (void*, FLOAT, FLOAT, DWRITE_UNDERLINE const*, IUnknown*) { return S_OK; } JUCE_COMRESULT DrawStrikethrough (void*, FLOAT, FLOAT, DWRITE_STRIKETHROUGH const*, IUnknown*) { return S_OK; } JUCE_COMRESULT DrawInlineObject (void*, FLOAT, FLOAT, IDWriteInlineObject*, BOOL, BOOL, IUnknown*) { return E_NOTIMPL; } JUCE_COMRESULT DrawGlyphRun (void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE, DWRITE_GLYPH_RUN const* glyphRun, DWRITE_GLYPH_RUN_DESCRIPTION const* runDescription, IUnknown* clientDrawingEffect) { TextLayout* const layout = static_cast (clientDrawingContext); if (! (baselineOriginY >= -1.0e10f && baselineOriginY <= 1.0e10f)) baselineOriginY = 0; // DirectWrite sometimes sends NaNs in this parameter if (baselineOriginY != lastOriginY) { lastOriginY = baselineOriginY; ++currentLine; if (currentLine >= layout->getNumLines()) { jassert (currentLine == layout->getNumLines()); TextLayout::Line* const newLine = new TextLayout::Line(); layout->addLine (newLine); newLine->lineOrigin = Point (baselineOriginX, baselineOriginY); } } TextLayout::Line& glyphLine = layout->getLine (currentLine); DWRITE_FONT_METRICS dwFontMetrics; glyphRun->fontFace->GetMetrics (&dwFontMetrics); glyphLine.ascent = jmax (glyphLine.ascent, scaledFontSize (dwFontMetrics.ascent, dwFontMetrics, glyphRun)); glyphLine.descent = jmax (glyphLine.descent, scaledFontSize (dwFontMetrics.descent, dwFontMetrics, glyphRun)); TextLayout::Run* const glyphRunLayout = new TextLayout::Run (Range (runDescription->textPosition, runDescription->textPosition + runDescription->stringLength), glyphRun->glyphCount); glyphLine.runs.add (glyphRunLayout); glyphRun->fontFace->GetMetrics (&dwFontMetrics); const float totalHeight = std::abs ((float) dwFontMetrics.ascent) + std::abs ((float) dwFontMetrics.descent); const float fontHeightToEmSizeFactor = (float) dwFontMetrics.designUnitsPerEm / totalHeight; glyphRunLayout->font = getFontForRun (glyphRun, glyphRun->fontEmSize / fontHeightToEmSizeFactor); glyphRunLayout->colour = getColourOf (static_cast (clientDrawingEffect)); const Point lineOrigin (layout->getLine (currentLine).lineOrigin); float x = baselineOriginX - lineOrigin.x; for (UINT32 i = 0; i < glyphRun->glyphCount; ++i) { const float advance = glyphRun->glyphAdvances[i]; if ((glyphRun->bidiLevel & 1) != 0) x -= advance; // RTL text glyphRunLayout->glyphs.add (TextLayout::Glyph (glyphRun->glyphIndices[i], Point (x, baselineOriginY - lineOrigin.y), advance)); if ((glyphRun->bidiLevel & 1) == 0) x += advance; // LTR text } return S_OK; } private: const AttributedString& attributedString; IDWriteFontCollection* const fontCollection; int currentLine; float lastOriginY; static float scaledFontSize (int n, const DWRITE_FONT_METRICS& metrics, const DWRITE_GLYPH_RUN* glyphRun) noexcept { return (std::abs ((float) n) / (float) metrics.designUnitsPerEm) * glyphRun->fontEmSize; } static Colour getColourOf (ID2D1SolidColorBrush* d2dBrush) { if (d2dBrush == nullptr) return Colours::black; const D2D1_COLOR_F colour (d2dBrush->GetColor()); return Colour::fromFloatRGBA (colour.r, colour.g, colour.b, colour.a); } Font getFontForRun (DWRITE_GLYPH_RUN const* glyphRun, float fontHeight) { for (int i = 0; i < attributedString.getNumAttributes(); ++i) if (const Font* font = attributedString.getAttribute(i)->getFont()) if (WindowsDirectWriteTypeface* wt = dynamic_cast (font->getTypeface())) if (wt->getIDWriteFontFace() == glyphRun->fontFace) return font->withHeight (fontHeight); ComSmartPtr dwFont; HRESULT hr = fontCollection->GetFontFromFontFace (glyphRun->fontFace, dwFont.resetAndGetPointerAddress()); jassert (dwFont != nullptr); ComSmartPtr dwFontFamily; hr = dwFont->GetFontFamily (dwFontFamily.resetAndGetPointerAddress()); return Font (getFontFamilyName (dwFontFamily), getFontFaceName (dwFont), fontHeight); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomDirectWriteTextRenderer) }; //================================================================================================== static float getFontHeightToEmSizeFactor (IDWriteFont* const dwFont) { ComSmartPtr dwFontFace; dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); if (dwFontFace == nullptr) return 1.0f; DWRITE_FONT_METRICS dwFontMetrics; dwFontFace->GetMetrics (&dwFontMetrics); const float totalHeight = (float) (std::abs (dwFontMetrics.ascent) + std::abs (dwFontMetrics.descent)); return dwFontMetrics.designUnitsPerEm / totalHeight; } void setTextFormatProperties (const AttributedString& text, IDWriteTextFormat* const format) { DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING; DWRITE_WORD_WRAPPING wrapType = DWRITE_WORD_WRAPPING_WRAP; switch (text.getJustification().getOnlyHorizontalFlags()) { case Justification::left: break; case Justification::right: alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break; case Justification::horizontallyCentred: alignment = DWRITE_TEXT_ALIGNMENT_CENTER; break; case Justification::horizontallyJustified: break; // DirectWrite cannot justify text, default to left alignment default: jassertfalse; break; // Illegal justification flags } switch (text.getWordWrap()) { case AttributedString::none: wrapType = DWRITE_WORD_WRAPPING_NO_WRAP; break; case AttributedString::byWord: break; case AttributedString::byChar: break; // DirectWrite doesn't support wrapping by character, default to word-wrap default: jassertfalse; break; // Illegal flags! } // DirectWrite does not automatically set reading direction // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic) if (text.getReadingDirection() == AttributedString::rightToLeft) { format->SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); switch (text.getJustification().getOnlyHorizontalFlags()) { case Justification::left: alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break; case Justification::right: alignment = DWRITE_TEXT_ALIGNMENT_LEADING; break; default: break; } } format->SetTextAlignment (alignment); format->SetWordWrapping (wrapType); } void addAttributedRange (const AttributedString::Attribute& attr, IDWriteTextLayout* textLayout, const int textLen, ID2D1RenderTarget* const renderTarget, IDWriteFontCollection* const fontCollection) { DWRITE_TEXT_RANGE range; range.startPosition = attr.range.getStart(); range.length = jmin (attr.range.getLength(), textLen - attr.range.getStart()); if (const Font* const font = attr.getFont()) { const String familyName (FontStyleHelpers::getConcreteFamilyName (*font)); BOOL fontFound = false; uint32 fontIndex; fontCollection->FindFamilyName (familyName.toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; ComSmartPtr fontFamily; HRESULT hr = fontCollection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); ComSmartPtr dwFont; uint32 fontFacesCount = 0; fontFacesCount = fontFamily->GetFontCount(); for (int i = fontFacesCount; --i >= 0;) { hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); if (font->getTypefaceStyle() == getFontFaceName (dwFont)) break; } textLayout->SetFontFamilyName (familyName.toWideCharPointer(), range); textLayout->SetFontWeight (dwFont->GetWeight(), range); textLayout->SetFontStretch (dwFont->GetStretch(), range); textLayout->SetFontStyle (dwFont->GetStyle(), range); const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); textLayout->SetFontSize (font->getHeight() * fontHeightToEmSizeFactor, range); } if (const Colour* const colour = attr.getColour()) { ComSmartPtr d2dBrush; renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (colour->getFloatRed(), colour->getFloatGreen(), colour->getFloatBlue(), colour->getFloatAlpha())), d2dBrush.resetAndGetPointerAddress()); // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours textLayout->SetDrawingEffect (d2dBrush, range); } } bool setupLayout (const AttributedString& text, const float maxWidth, const float maxHeight, ID2D1RenderTarget* const renderTarget, IDWriteFactory* const directWriteFactory, IDWriteFontCollection* const fontCollection, ComSmartPtr& textLayout) { // To add color to text, we need to create a D2D render target // Since we are not actually rendering to a D2D context we create a temporary GDI render target Font defaultFont; BOOL fontFound = false; uint32 fontIndex; fontCollection->FindFamilyName (defaultFont.getTypeface()->getName().toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; ComSmartPtr dwFontFamily; HRESULT hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); ComSmartPtr dwFont; hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, dwFont.resetAndGetPointerAddress()); const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); jassert (directWriteFactory != nullptr); ComSmartPtr dwTextFormat; hr = directWriteFactory->CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), fontCollection, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, L"en-us", dwTextFormat.resetAndGetPointerAddress()); setTextFormatProperties (text, dwTextFormat); { DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 }; ComSmartPtr trimmingSign; hr = directWriteFactory->CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress()); hr = dwTextFormat->SetTrimming (&trimming, trimmingSign); } const int textLen = text.getText().length(); hr = directWriteFactory->CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat, maxWidth, maxHeight, textLayout.resetAndGetPointerAddress()); if (FAILED (hr) || textLayout == nullptr) return false; const int numAttributes = text.getNumAttributes(); for (int i = 0; i < numAttributes; ++i) addAttributedRange (*text.getAttribute (i), textLayout, textLen, renderTarget, fontCollection); return true; } void createLayout (TextLayout& layout, const AttributedString& text, IDWriteFactory* const directWriteFactory, ID2D1Factory* const direct2dFactory, IDWriteFontCollection* const fontCollection) { // To add color to text, we need to create a D2D render target // Since we are not actually rendering to a D2D context we create a temporary GDI render target D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties (D2D1_RENDER_TARGET_TYPE_SOFTWARE, D2D1::PixelFormat (DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 0, 0, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, D2D1_FEATURE_LEVEL_DEFAULT); ComSmartPtr renderTarget; HRESULT hr = direct2dFactory->CreateDCRenderTarget (&d2dRTProp, renderTarget.resetAndGetPointerAddress()); ComSmartPtr dwTextLayout; if (! setupLayout (text, layout.getWidth(), layout.getHeight(), renderTarget, directWriteFactory, fontCollection, dwTextLayout)) return; UINT32 actualLineCount = 0; hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); layout.ensureStorageAllocated (actualLineCount); { ComSmartPtr textRenderer (new CustomDirectWriteTextRenderer (fontCollection, text)); hr = dwTextLayout->Draw (&layout, textRenderer, 0, 0); } HeapBlock dwLineMetrics (actualLineCount); hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount); int lastLocation = 0; const int numLines = jmin ((int) actualLineCount, layout.getNumLines()); for (int i = 0; i < numLines; ++i) { layout.getLine(i).stringRange = Range (lastLocation, (int) lastLocation + dwLineMetrics[i].length); lastLocation += dwLineMetrics[i].length; } } void drawToD2DContext (const AttributedString& text, const Rectangle& area, ID2D1RenderTarget* const renderTarget, IDWriteFactory* const directWriteFactory, IDWriteFontCollection* const fontCollection) { ComSmartPtr dwTextLayout; if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, directWriteFactory, fontCollection, dwTextLayout)) { ComSmartPtr d2dBrush; renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f)), d2dBrush.resetAndGetPointerAddress()); renderTarget->DrawTextLayout (D2D1::Point2F ((float) area.getX(), (float) area.getY()), dwTextLayout, d2dBrush, D2D1_DRAW_TEXT_OPTIONS_CLIP); } } } static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) { const int numCharacterAttributes = text.getNumAttributes(); for (int i = 0; i < numCharacterAttributes; ++i) if (const Font* const font = text.getAttribute (i)->getFont()) if (dynamic_cast (font->getTypeface()) == nullptr) return false; return true; } #endif bool TextLayout::createNativeLayout (const AttributedString& text) { #if JUCE_USE_DIRECTWRITE if (! canAllTypefacesBeUsedInLayout (text)) return false; SharedResourcePointer factories; if (factories->d2dFactory != nullptr && factories->systemFonts != nullptr) { #if JUCE_64BIT // There's a mysterious bug in 64-bit Windows that causes garbage floating-point // values to be returned to DrawGlyphRun the first time that it gets used. // In lieu of a better plan, this bodge uses a dummy call to work around this. static bool hasBeenCalled = false; if (! hasBeenCalled) { hasBeenCalled = true; TextLayout dummy; DirectWriteTypeLayout::createLayout (dummy, text, factories->directWriteFactory, factories->d2dFactory, factories->systemFonts); } #endif DirectWriteTypeLayout::createLayout (*this, text, factories->directWriteFactory, factories->d2dFactory, factories->systemFonts); return true; } #else (void) text; #endif return false; } juce_win32_DirectWriteTypeface.cpp000066400000000000000000000267231320201440200342750ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_DIRECTWRITE namespace { static String getLocalisedName (IDWriteLocalizedStrings* names) { jassert (names != nullptr); uint32 index = 0; BOOL exists = false; HRESULT hr = names->FindLocaleName (L"en-us", &index, &exists); if (! exists) index = 0; uint32 length = 0; hr = names->GetStringLength (index, &length); HeapBlock name (length + 1); hr = names->GetString (index, name, length + 1); return static_cast (name); } static String getFontFamilyName (IDWriteFontFamily* family) { jassert (family != nullptr); ComSmartPtr familyNames; HRESULT hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); jassert (SUCCEEDED (hr)); (void) hr; return getLocalisedName (familyNames); } static String getFontFaceName (IDWriteFont* font) { jassert (font != nullptr); ComSmartPtr faceNames; HRESULT hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); jassert (SUCCEEDED (hr)); (void) hr; return getLocalisedName (faceNames); } } class Direct2DFactories { public: Direct2DFactories() { if (direct2dDll.open ("d2d1.dll")) { JUCE_LOAD_WINAPI_FUNCTION (direct2dDll, D2D1CreateFactory, d2d1CreateFactory, HRESULT, (D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**)) if (d2d1CreateFactory != nullptr) { D2D1_FACTORY_OPTIONS options; options.debugLevel = D2D1_DEBUG_LEVEL_NONE; d2d1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof (ID2D1Factory), &options, (void**) d2dFactory.resetAndGetPointerAddress()); } } if (directWriteDll.open ("DWrite.dll")) { JUCE_LOAD_WINAPI_FUNCTION (directWriteDll, DWriteCreateFactory, dWriteCreateFactory, HRESULT, (DWRITE_FACTORY_TYPE, REFIID, IUnknown**)) if (dWriteCreateFactory != nullptr) { dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), (IUnknown**) directWriteFactory.resetAndGetPointerAddress()); if (directWriteFactory != nullptr) directWriteFactory->GetSystemFontCollection (systemFonts.resetAndGetPointerAddress()); } } } ~Direct2DFactories() { d2dFactory = nullptr; // (need to make sure these are released before deleting the DynamicLibrary objects) directWriteFactory = nullptr; systemFonts = nullptr; } ComSmartPtr d2dFactory; ComSmartPtr directWriteFactory; ComSmartPtr systemFonts; private: DynamicLibrary direct2dDll, directWriteDll; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories) }; //================================================================================================== class WindowsDirectWriteTypeface : public Typeface { public: WindowsDirectWriteTypeface (const Font& font, IDWriteFontCollection* fontCollection) : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), unitsToHeightScaleFactor (1.0f), heightToPointsFactor (1.0f), ascent (0.0f) { jassert (fontCollection != nullptr); BOOL fontFound = false; uint32 fontIndex = 0; HRESULT hr = fontCollection->FindFamilyName (font.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; // Get the font family using the search results // Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family ComSmartPtr dwFontFamily; hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); // Get a specific font in the font family using typeface style { ComSmartPtr dwFont; for (int i = (int) dwFontFamily->GetFontCount(); --i >= 0;) { hr = dwFontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); if (i == 0) break; ComSmartPtr faceNames; hr = dwFont->GetFaceNames (faceNames.resetAndGetPointerAddress()); if (font.getTypefaceStyle() == getLocalisedName (faceNames)) break; } jassert (dwFont != nullptr); hr = dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); } if (dwFontFace != nullptr) { DWRITE_FONT_METRICS dwFontMetrics; dwFontFace->GetMetrics (&dwFontMetrics); // All Font Metrics are in design units so we need to get designUnitsPerEm value // to get the metrics into Em/Design Independent Pixels designUnitsPerEm = dwFontMetrics.designUnitsPerEm; ascent = std::abs ((float) dwFontMetrics.ascent); const float totalSize = ascent + std::abs ((float) dwFontMetrics.descent); ascent /= totalSize; unitsToHeightScaleFactor = designUnitsPerEm / totalSize; HDC tempDC = GetDC (0); float dpi = (GetDeviceCaps (tempDC, LOGPIXELSX) + GetDeviceCaps (tempDC, LOGPIXELSY)) / 2.0f; heightToPointsFactor = (dpi / GetDeviceCaps (tempDC, LOGPIXELSY)) * unitsToHeightScaleFactor; ReleaseDC (0, tempDC); const float pathAscent = (1024.0f * dwFontMetrics.ascent) / designUnitsPerEm; const float pathDescent = (1024.0f * dwFontMetrics.descent) / designUnitsPerEm; const float pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent)); pathTransform = AffineTransform::scale (pathScale); } } bool loadedOk() const noexcept { return dwFontFace != nullptr; } float getAscent() const { return ascent; } float getDescent() const { return 1.0f - ascent; } float getHeightToPointsFactor() const { return heightToPointsFactor; } float getStringWidth (const String& text) { const CharPointer_UTF32 textUTF32 (text.toUTF32()); const size_t len = textUTF32.length(); HeapBlock glyphIndices (len); dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices); HeapBlock dwGlyphMetrics (len); dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false); float x = 0; for (size_t i = 0; i < len; ++i) x += (float) dwGlyphMetrics[i].advanceWidth / designUnitsPerEm; return x * unitsToHeightScaleFactor; } void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) { xOffsets.add (0); const CharPointer_UTF32 textUTF32 (text.toUTF32()); const size_t len = textUTF32.length(); HeapBlock glyphIndices (len); dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices); HeapBlock dwGlyphMetrics (len); dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false); float x = 0; for (size_t i = 0; i < len; ++i) { x += (float) dwGlyphMetrics[i].advanceWidth / designUnitsPerEm; xOffsets.add (x * unitsToHeightScaleFactor); resultGlyphs.add (glyphIndices[i]); } } bool getOutlineForGlyph (int glyphNumber, Path& path) { jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty UINT16 glyphIndex = (UINT16) glyphNumber; ComSmartPtr pathGeometrySink (new PathGeometrySink()); dwFontFace->GetGlyphRunOutline (1024.0f, &glyphIndex, nullptr, nullptr, 1, false, false, pathGeometrySink); path = pathGeometrySink->path; if (! pathTransform.isIdentity()) path.applyTransform (pathTransform); return true; } IDWriteFontFace* getIDWriteFontFace() const noexcept { return dwFontFace; } private: SharedResourcePointer factories; ComSmartPtr dwFontFace; float unitsToHeightScaleFactor, heightToPointsFactor, ascent; int designUnitsPerEm; AffineTransform pathTransform; class PathGeometrySink : public ComBaseClassHelper { public: PathGeometrySink() : ComBaseClassHelper (0) {} void __stdcall AddBeziers (const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) { for (UINT i = 0; i < beziersCount; ++i) path.cubicTo ((float) beziers[i].point1.x, (float) beziers[i].point1.y, (float) beziers[i].point2.x, (float) beziers[i].point2.y, (float) beziers[i].point3.x, (float) beziers[i].point3.y); } void __stdcall AddLines (const D2D1_POINT_2F* points, UINT pointsCount) { for (UINT i = 0; i < pointsCount; ++i) path.lineTo ((float) points[i].x, (float) points[i].y); } void __stdcall BeginFigure (D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN) { path.startNewSubPath ((float) startPoint.x, (float) startPoint.y); } void __stdcall EndFigure (D2D1_FIGURE_END figureEnd) { if (figureEnd == D2D1_FIGURE_END_CLOSED) path.closeSubPath(); } void __stdcall SetFillMode (D2D1_FILL_MODE fillMode) { path.setUsingNonZeroWinding (fillMode == D2D1_FILL_MODE_WINDING); } void __stdcall SetSegmentFlags (D2D1_PATH_SEGMENT) {} JUCE_COMRESULT Close() { return S_OK; } Path path; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathGeometrySink) }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDirectWriteTypeface) }; #endif libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp000066400000000000000000000537341320201440200315410ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ /* This is some quick-and-dirty code to extract the typeface name from a lump of TTF file data. It's needed because although win32 will happily load a TTF file from in-memory data, it won't tell you the name of the damned font that it just loaded.. and in order to actually use the font, you need to know its name!! Anyway, this awful hack seems to work for most fonts. */ namespace TTFNameExtractor { struct OffsetTable { uint32 version; uint16 numTables, searchRange, entrySelector, rangeShift; }; struct TableDirectory { char tag[4]; uint32 checkSum, offset, length; }; struct NamingTable { uint16 formatSelector; uint16 numberOfNameRecords; uint16 offsetStartOfStringStorage; }; struct NameRecord { uint16 platformID, encodingID, languageID; uint16 nameID, stringLength, offsetFromStorageArea; }; static String parseNameRecord (MemoryInputStream& input, const NameRecord& nameRecord, const int64 directoryOffset, const int64 offsetOfStringStorage) { String result; const int64 oldPos = input.getPosition(); input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea)); const int stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength); const int platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID); if (platformID == 0 || platformID == 3) { const int numChars = stringLength / 2 + 1; HeapBlock buffer; buffer.calloc (numChars + 1); input.read (buffer, stringLength); for (int i = 0; i < numChars; ++i) buffer[i] = ByteOrder::swapIfLittleEndian (buffer[i]); static_jassert (sizeof (CharPointer_UTF16::CharType) == sizeof (uint16)); result = CharPointer_UTF16 ((CharPointer_UTF16::CharType*) buffer.getData()); } else { HeapBlock buffer; buffer.calloc (stringLength + 1); input.read (buffer, stringLength); result = CharPointer_UTF8 (buffer.getData()); } input.setPosition (oldPos); return result; } static String parseNameTable (MemoryInputStream& input, int64 directoryOffset) { input.setPosition (directoryOffset); NamingTable namingTable = { 0 }; input.read (&namingTable, sizeof (namingTable)); for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (namingTable.numberOfNameRecords); ++i) { NameRecord nameRecord = { 0 }; input.read (&nameRecord, sizeof (nameRecord)); if (ByteOrder::swapIfLittleEndian (nameRecord.nameID) == 4) { const String result (parseNameRecord (input, nameRecord, directoryOffset, ByteOrder::swapIfLittleEndian (namingTable.offsetStartOfStringStorage))); if (result.isNotEmpty()) return result; } } return String(); } static String getTypefaceNameFromFile (MemoryInputStream& input) { OffsetTable offsetTable = { 0 }; input.read (&offsetTable, sizeof (offsetTable)); for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i) { TableDirectory tableDirectory; zerostruct (tableDirectory); input.read (&tableDirectory, sizeof (tableDirectory)); if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name")) return parseNameTable (input, ByteOrder::swapIfLittleEndian (tableDirectory.offset)); } return String(); } } namespace FontEnumerators { static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) { if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0) { const String fontName (lpelfe->elfLogFont.lfFaceName); ((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@")); } return 1; } static int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) { if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0) { LOGFONTW lf = { 0 }; lf.lfWeight = FW_DONTCARE; lf.lfOutPrecision = OUT_OUTLINE_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfCharSet = DEFAULT_CHARSET; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfPitchAndFamily = FF_DONTCARE; const String fontName (lpelfe->elfLogFont.lfFaceName); fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName)); HDC dc = CreateCompatibleDC (0); EnumFontFamiliesEx (dc, &lf, (FONTENUMPROCW) &fontEnum2, lParam, 0); DeleteDC (dc); } return 1; } } StringArray Font::findAllTypefaceNames() { StringArray results; #if JUCE_USE_DIRECTWRITE SharedResourcePointer factories; if (factories->systemFonts != nullptr) { ComSmartPtr fontFamily; uint32 fontFamilyCount = 0; fontFamilyCount = factories->systemFonts->GetFontFamilyCount(); for (uint32 i = 0; i < fontFamilyCount; ++i) { HRESULT hr = factories->systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress()); if (SUCCEEDED (hr)) results.addIfNotAlreadyThere (getFontFamilyName (fontFamily)); } } else #endif { HDC dc = CreateCompatibleDC (0); { LOGFONTW lf = { 0 }; lf.lfWeight = FW_DONTCARE; lf.lfOutPrecision = OUT_OUTLINE_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfCharSet = DEFAULT_CHARSET; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfPitchAndFamily = FF_DONTCARE; EnumFontFamiliesEx (dc, &lf, (FONTENUMPROCW) &FontEnumerators::fontEnum1, (LPARAM) &results, 0); } DeleteDC (dc); } results.sort (true); return results; } StringArray Font::findAllTypefaceStyles (const String& family) { if (FontStyleHelpers::isPlaceholderFamilyName (family)) return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); StringArray results; #if JUCE_USE_DIRECTWRITE SharedResourcePointer factories; if (factories->systemFonts != nullptr) { BOOL fontFound = false; uint32 fontIndex = 0; HRESULT hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; // Get the font family using the search results // Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family ComSmartPtr fontFamily; hr = factories->systemFonts->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); // Get the font faces ComSmartPtr dwFont; uint32 fontFacesCount = 0; fontFacesCount = fontFamily->GetFontCount(); for (uint32 i = 0; i < fontFacesCount; ++i) { hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); // Ignore any algorithmically generated bold and oblique styles.. if (dwFont->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE) results.addIfNotAlreadyThere (getFontFaceName (dwFont)); } } else #endif { results.add ("Regular"); results.add ("Italic"); results.add ("Bold"); results.add ("Bold Italic"); } return results; } extern bool juce_isRunningInWine(); struct DefaultFontNames { DefaultFontNames() { if (juce_isRunningInWine()) { // If we're running in Wine, then use fonts that might be available on Linux.. defaultSans = "Bitstream Vera Sans"; defaultSerif = "Bitstream Vera Serif"; defaultFixed = "Bitstream Vera Sans Mono"; } else { defaultSans = "Verdana"; defaultSerif = "Times New Roman"; defaultFixed = "Lucida Console"; defaultFallback = "Tahoma"; // (contains plenty of unicode characters) } } String defaultSans, defaultSerif, defaultFixed, defaultFallback; }; Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; Font newFont (font); const String& faceName = font.getTypefaceName(); if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed); if (font.getTypefaceStyle() == getDefaultStyle()) newFont.setTypefaceStyle ("Regular"); return Typeface::createSystemTypefaceFor (newFont); } //============================================================================== class WindowsTypeface : public Typeface { public: WindowsTypeface (const Font& font) : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), fontH (0), previousFontH (0), dc (CreateCompatibleDC (0)), memoryFont (0), ascent (1.0f), heightToPointsFactor (1.0f), defaultGlyph (-1) { loadFont(); } WindowsTypeface (const void* data, size_t dataSize) : Typeface (String(), String()), fontH (0), previousFontH (0), dc (CreateCompatibleDC (0)), memoryFont (0), ascent (1.0f), heightToPointsFactor (1.0f), defaultGlyph (-1) { DWORD numInstalled = 0; memoryFont = AddFontMemResourceEx (const_cast (data), (DWORD) dataSize, nullptr, &numInstalled); MemoryInputStream m (data, dataSize, false); name = TTFNameExtractor::getTypefaceNameFromFile (m); loadFont(); } ~WindowsTypeface() { SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker DeleteDC (dc); if (fontH != 0) DeleteObject (fontH); if (memoryFont != 0) RemoveFontMemResourceEx (memoryFont); } float getAscent() const { return ascent; } float getDescent() const { return 1.0f - ascent; } float getHeightToPointsFactor() const { return heightToPointsFactor; } float getStringWidth (const String& text) { const CharPointer_UTF16 utf16 (text.toUTF16()); const size_t numChars = utf16.length(); HeapBlock results (numChars + 1); results[numChars] = -1; float x = 0; if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast (results.getData()), GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) { for (size_t i = 0; i < numChars; ++i) x += getKerning (dc, results[i], results[i + 1]); } return x; } void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) { const CharPointer_UTF16 utf16 (text.toUTF16()); const size_t numChars = utf16.length(); HeapBlock results (numChars + 1); results[numChars] = -1; float x = 0; if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast (results.getData()), GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) { resultGlyphs.ensureStorageAllocated ((int) numChars); xOffsets.ensureStorageAllocated ((int) numChars + 1); for (size_t i = 0; i < numChars; ++i) { resultGlyphs.add (results[i]); xOffsets.add (x); x += getKerning (dc, results[i], results[i + 1]); } } xOffsets.add (x); } bool getOutlineForGlyph (int glyphNumber, Path& glyphPath) { if (glyphNumber < 0) glyphNumber = defaultGlyph; GLYPHMETRICS gm; // (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..) const int bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix); if (bufSize > 0) { HeapBlock data (bufSize); GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, bufSize, data, &identityMatrix); const TTPOLYGONHEADER* pheader = reinterpret_cast (data.getData()); const float scaleX = 1.0f / tm.tmHeight; const float scaleY = -scaleX; while ((char*) pheader < data + bufSize) { glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value, scaleY * pheader->pfxStart.y.value); const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); const char* const curveEnd = ((const char*) pheader) + pheader->cb; while ((const char*) curve < curveEnd) { if (curve->wType == TT_PRIM_LINE) { for (int i = 0; i < curve->cpfx; ++i) glyphPath.lineTo (scaleX * curve->apfx[i].x.value, scaleY * curve->apfx[i].y.value); } else if (curve->wType == TT_PRIM_QSPLINE) { for (int i = 0; i < curve->cpfx - 1; ++i) { const float x2 = scaleX * curve->apfx[i].x.value; const float y2 = scaleY * curve->apfx[i].y.value; float x3 = scaleX * curve->apfx[i + 1].x.value; float y3 = scaleY * curve->apfx[i + 1].y.value; if (i < curve->cpfx - 2) { x3 = 0.5f * (x2 + x3); y3 = 0.5f * (y2 + y3); } glyphPath.quadraticTo (x2, y2, x3, y3); } } curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); } pheader = (const TTPOLYGONHEADER*) curve; glyphPath.closeSubPath(); } } return true; } private: static const MAT2 identityMatrix; HFONT fontH; HGDIOBJ previousFontH; HDC dc; TEXTMETRIC tm; HANDLE memoryFont; float ascent, heightToPointsFactor; int defaultGlyph, heightInPoints; struct KerningPair { int glyph1, glyph2; float kerning; bool operator== (const KerningPair& other) const noexcept { return glyph1 == other.glyph1 && glyph2 == other.glyph2; } bool operator< (const KerningPair& other) const noexcept { return glyph1 < other.glyph1 || (glyph1 == other.glyph1 && glyph2 < other.glyph2); } }; SortedSet kerningPairs; void loadFont() { SetMapperFlags (dc, 0); SetMapMode (dc, MM_TEXT); LOGFONTW lf = { 0 }; lf.lfCharSet = DEFAULT_CHARSET; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfOutPrecision = OUT_OUTLINE_PRECIS; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; lf.lfQuality = PROOF_QUALITY; lf.lfItalic = (BYTE) (style.contains ("Italic") ? TRUE : FALSE); lf.lfWeight = style.contains ("Bold") ? FW_BOLD : FW_NORMAL; lf.lfHeight = -256; name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName)); HFONT standardSizedFont = CreateFontIndirect (&lf); if (standardSizedFont != 0) { if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0) { fontH = standardSizedFont; OUTLINETEXTMETRIC otm; if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0) { heightInPoints = otm.otmEMSquare; lf.lfHeight = -(int) heightInPoints; fontH = CreateFontIndirect (&lf); SelectObject (dc, fontH); DeleteObject (standardSizedFont); } } } if (GetTextMetrics (dc, &tm)) { float dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f; heightToPointsFactor = (dpi / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight; ascent = tm.tmAscent / (float) tm.tmHeight; defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar); createKerningPairs (dc, (float) tm.tmHeight); } } void createKerningPairs (HDC dc, const float height) { HeapBlock rawKerning; const DWORD numKPs = GetKerningPairs (dc, 0, 0); rawKerning.calloc (numKPs); GetKerningPairs (dc, numKPs, rawKerning); kerningPairs.ensureStorageAllocated ((int) numKPs); for (DWORD i = 0; i < numKPs; ++i) { KerningPair kp; kp.glyph1 = getGlyphForChar (dc, rawKerning[i].wFirst); kp.glyph2 = getGlyphForChar (dc, rawKerning[i].wSecond); const int standardWidth = getGlyphWidth (dc, kp.glyph1); kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height; kerningPairs.add (kp); kp.glyph2 = -1; // add another entry for the standard width version.. kp.kerning = standardWidth / height; kerningPairs.add (kp); } } static int getGlyphForChar (HDC dc, juce_wchar character) { const WCHAR charToTest[] = { (WCHAR) character, 0 }; WORD index = 0; if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR || index == 0xffff) return -1; return index; } static int getGlyphWidth (HDC dc, int glyphNumber) { GLYPHMETRICS gm; gm.gmCellIncX = 0; GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix); return gm.gmCellIncX; } float getKerning (HDC dc, const int glyph1, const int glyph2) { KerningPair kp; kp.glyph1 = glyph1; kp.glyph2 = glyph2; int index = kerningPairs.indexOf (kp); if (index < 0) { kp.glyph2 = -1; index = kerningPairs.indexOf (kp); if (index < 0) { kp.glyph2 = -1; kp.kerning = getGlyphWidth (dc, kp.glyph1) / (float) tm.tmHeight; kerningPairs.add (kp); return kp.kerning; } } return kerningPairs.getReference (index).kerning; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface) }; const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { #if JUCE_USE_DIRECTWRITE SharedResourcePointer factories; if (factories->systemFonts != nullptr) { ScopedPointer wtf (new WindowsDirectWriteTypeface (font, factories->systemFonts)); if (wtf->loadedOk()) return wtf.release(); } #endif return new WindowsTypeface (font); } Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) { return new WindowsTypeface (data, dataSize); } void Typeface::scanFolderForFonts (const File&) { jassertfalse; // not implemented on this platform } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/placement/000077500000000000000000000000001320201440200263625ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.h000066400000000000000000000205211320201440200323540ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_JUSTIFICATION_H_INCLUDED #define JUCE_JUSTIFICATION_H_INCLUDED //============================================================================== /** Represents a type of justification to be used when positioning graphical items. e.g. it indicates whether something should be placed top-left, top-right, centred, etc. It is used in various places wherever this kind of information is needed. */ class Justification { public: //============================================================================== /** Creates a Justification object using a combination of flags from the Flags enum. */ Justification (int justificationFlags) noexcept : flags (justificationFlags) {} /** Creates a copy of another Justification object. */ Justification (const Justification& other) noexcept : flags (other.flags) {} /** Copies another Justification object. */ Justification& operator= (const Justification& other) noexcept { flags = other.flags; return *this; } bool operator== (const Justification& other) const noexcept { return flags == other.flags; } bool operator!= (const Justification& other) const noexcept { return flags != other.flags; } //============================================================================== /** Returns the raw flags that are set for this Justification object. */ inline int getFlags() const noexcept { return flags; } /** Tests a set of flags for this object. @returns true if any of the flags passed in are set on this object. */ inline bool testFlags (int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } /** Returns just the flags from this object that deal with vertical layout. */ int getOnlyVerticalFlags() const noexcept { return flags & (top | bottom | verticallyCentred); } /** Returns just the flags from this object that deal with horizontal layout. */ int getOnlyHorizontalFlags() const noexcept { return flags & (left | right | horizontallyCentred | horizontallyJustified); } //============================================================================== /** Adjusts the position of a rectangle to fit it into a space. The (x, y) position of the rectangle will be updated to position it inside the given space according to the justification flags. */ template void applyToRectangle (ValueType& x, ValueType& y, ValueType w, ValueType h, ValueType spaceX, ValueType spaceY, ValueType spaceW, ValueType spaceH) const noexcept { x = spaceX; if ((flags & horizontallyCentred) != 0) x += (spaceW - w) / (ValueType) 2; else if ((flags & right) != 0) x += spaceW - w; y = spaceY; if ((flags & verticallyCentred) != 0) y += (spaceH - h) / (ValueType) 2; else if ((flags & bottom) != 0) y += spaceH - h; } /** Returns the new position of a rectangle that has been justified to fit within a given space. */ template const Rectangle appliedToRectangle (const Rectangle& areaToAdjust, const Rectangle& targetSpace) const noexcept { ValueType x = areaToAdjust.getX(), y = areaToAdjust.getY(); applyToRectangle (x, y, areaToAdjust.getWidth(), areaToAdjust.getHeight(), targetSpace.getX(), targetSpace.getY(), targetSpace.getWidth(), targetSpace.getHeight()); return areaToAdjust.withPosition (x, y); } //============================================================================== /** Flag values that can be combined and used in the constructor. */ enum Flags { //============================================================================== /** Indicates that the item should be aligned against the left edge of the available space. */ left = 1, /** Indicates that the item should be aligned against the right edge of the available space. */ right = 2, /** Indicates that the item should be placed in the centre between the left and right sides of the available space. */ horizontallyCentred = 4, //============================================================================== /** Indicates that the item should be aligned against the top edge of the available space. */ top = 8, /** Indicates that the item should be aligned against the bottom edge of the available space. */ bottom = 16, /** Indicates that the item should be placed in the centre between the top and bottom sides of the available space. */ verticallyCentred = 32, //============================================================================== /** Indicates that lines of text should be spread out to fill the maximum width available, so that both margins are aligned vertically. */ horizontallyJustified = 64, //============================================================================== /** Indicates that the item should be centred vertically and horizontally. This is equivalent to (horizontallyCentred | verticallyCentred) */ centred = 36, /** Indicates that the item should be centred vertically but placed on the left hand side. This is equivalent to (left | verticallyCentred) */ centredLeft = 33, /** Indicates that the item should be centred vertically but placed on the right hand side. This is equivalent to (right | verticallyCentred) */ centredRight = 34, /** Indicates that the item should be centred horizontally and placed at the top. This is equivalent to (horizontallyCentred | top) */ centredTop = 12, /** Indicates that the item should be centred horizontally and placed at the bottom. This is equivalent to (horizontallyCentred | bottom) */ centredBottom = 20, /** Indicates that the item should be placed in the top-left corner. This is equivalent to (left | top) */ topLeft = 9, /** Indicates that the item should be placed in the top-right corner. This is equivalent to (right | top) */ topRight = 10, /** Indicates that the item should be placed in the bottom-left corner. This is equivalent to (left | bottom) */ bottomLeft = 17, /** Indicates that the item should be placed in the bottom-left corner. This is equivalent to (right | bottom) */ bottomRight = 18 }; private: //============================================================================== int flags; }; #endif // JUCE_JUSTIFICATION_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp000066400000000000000000000100451320201440200336310ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ RectanglePlacement::RectanglePlacement (const RectanglePlacement& other) noexcept : flags (other.flags) { } RectanglePlacement& RectanglePlacement::operator= (const RectanglePlacement& other) noexcept { flags = other.flags; return *this; } bool RectanglePlacement::operator== (const RectanglePlacement& other) const noexcept { return flags == other.flags; } bool RectanglePlacement::operator!= (const RectanglePlacement& other) const noexcept { return flags != other.flags; } void RectanglePlacement::applyTo (double& x, double& y, double& w, double& h, const double dx, const double dy, const double dw, const double dh) const noexcept { if (w == 0 || h == 0) return; if ((flags & stretchToFit) != 0) { x = dx; y = dy; w = dw; h = dh; } else { double scale = (flags & fillDestination) != 0 ? jmax (dw / w, dh / h) : jmin (dw / w, dh / h); if ((flags & onlyReduceInSize) != 0) scale = jmin (scale, 1.0); if ((flags & onlyIncreaseInSize) != 0) scale = jmax (scale, 1.0); w *= scale; h *= scale; if ((flags & xLeft) != 0) x = dx; else if ((flags & xRight) != 0) x = dx + dw - w; else x = dx + (dw - w) * 0.5; if ((flags & yTop) != 0) y = dy; else if ((flags & yBottom) != 0) y = dy + dh - h; else y = dy + (dh - h) * 0.5; } } AffineTransform RectanglePlacement::getTransformToFit (const Rectangle& source, const Rectangle& destination) const noexcept { if (source.isEmpty()) return AffineTransform::identity; float newX = destination.getX(); float newY = destination.getY(); float scaleX = destination.getWidth() / source.getWidth(); float scaleY = destination.getHeight() / source.getHeight(); if ((flags & stretchToFit) == 0) { scaleX = (flags & fillDestination) != 0 ? jmax (scaleX, scaleY) : jmin (scaleX, scaleY); if ((flags & onlyReduceInSize) != 0) scaleX = jmin (scaleX, 1.0f); if ((flags & onlyIncreaseInSize) != 0) scaleX = jmax (scaleX, 1.0f); scaleY = scaleX; if ((flags & xRight) != 0) newX += destination.getWidth() - source.getWidth() * scaleX; // right else if ((flags & xLeft) == 0) newX += (destination.getWidth() - source.getWidth() * scaleX) / 2.0f; // centre if ((flags & yBottom) != 0) newY += destination.getHeight() - source.getHeight() * scaleX; // bottom else if ((flags & yTop) == 0) newY += (destination.getHeight() - source.getHeight() * scaleX) / 2.0f; // centre } return AffineTransform::translation (-source.getX(), -source.getY()) .scaled (scaleX, scaleY) .translated (newX, newY); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.h000066400000000000000000000176101320201440200333030ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_RECTANGLEPLACEMENT_H_INCLUDED #define JUCE_RECTANGLEPLACEMENT_H_INCLUDED //============================================================================== /** Defines the method used to postion some kind of rectangular object within a rectangular viewport. Although similar to Justification, this is more specific, and has some extra options. */ class JUCE_API RectanglePlacement { public: //============================================================================== /** Creates a RectanglePlacement object using a combination of flags from the Flags enum. */ inline RectanglePlacement (int placementFlags) noexcept : flags (placementFlags) {} /** Creates a default RectanglePlacement object, which is equivalent to using the 'centred' flag. */ inline RectanglePlacement() noexcept : flags (centred) {} /** Creates a copy of another RectanglePlacement object. */ RectanglePlacement (const RectanglePlacement&) noexcept; /** Copies another RectanglePlacement object. */ RectanglePlacement& operator= (const RectanglePlacement&) noexcept; bool operator== (const RectanglePlacement&) const noexcept; bool operator!= (const RectanglePlacement&) const noexcept; //============================================================================== /** Flag values that can be combined and used in the constructor. */ enum Flags { //============================================================================== /** Indicates that the source rectangle's left edge should be aligned with the left edge of the target rectangle. */ xLeft = 1, /** Indicates that the source rectangle's right edge should be aligned with the right edge of the target rectangle. */ xRight = 2, /** Indicates that the source should be placed in the centre between the left and right sides of the available space. */ xMid = 4, //============================================================================== /** Indicates that the source's top edge should be aligned with the top edge of the destination rectangle. */ yTop = 8, /** Indicates that the source's bottom edge should be aligned with the bottom edge of the destination rectangle. */ yBottom = 16, /** Indicates that the source should be placed in the centre between the top and bottom sides of the available space. */ yMid = 32, //============================================================================== /** If this flag is set, then the source rectangle will be resized to completely fill the destination rectangle, and all other flags are ignored. */ stretchToFit = 64, //============================================================================== /** If this flag is set, then the source rectangle will be resized so that it is the minimum size to completely fill the destination rectangle, without changing its aspect ratio. This means that some of the source rectangle may fall outside the destination. If this flag is not set, the source will be given the maximum size at which none of it falls outside the destination rectangle. */ fillDestination = 128, /** Indicates that the source rectangle can be reduced in size if required, but should never be made larger than its original size. */ onlyReduceInSize = 256, /** Indicates that the source rectangle can be enlarged if required, but should never be made smaller than its original size. */ onlyIncreaseInSize = 512, /** Indicates that the source rectangle's size should be left unchanged. */ doNotResize = (onlyIncreaseInSize | onlyReduceInSize), //============================================================================== /** A shorthand value that is equivalent to (xMid | yMid). */ centred = 4 + 32 }; //============================================================================== /** Returns the raw flags that are set for this object. */ inline int getFlags() const noexcept { return flags; } /** Tests a set of flags for this object. @returns true if any of the flags passed in are set on this object. */ inline bool testFlags (int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } //============================================================================== /** Adjusts the position and size of a rectangle to fit it into a space. The source rectangle coordinates will be adjusted so that they fit into the destination rectangle based on this object's flags. */ void applyTo (double& sourceX, double& sourceY, double& sourceW, double& sourceH, double destinationX, double destinationY, double destinationW, double destinationH) const noexcept; /** Returns the rectangle that should be used to fit the given source rectangle into the destination rectangle using the current flags. */ template Rectangle appliedTo (const Rectangle& source, const Rectangle& destination) const noexcept { double x = source.getX(), y = source.getY(), w = source.getWidth(), h = source.getHeight(); applyTo (x, y, w, h, static_cast (destination.getX()), static_cast (destination.getY()), static_cast (destination.getWidth()), static_cast (destination.getHeight())); return Rectangle (static_cast (x), static_cast (y), static_cast (w), static_cast (h)); } /** Returns the transform that should be applied to these source coordinates to fit them into the destination rectangle using the current flags. */ AffineTransform getTransformToFit (const Rectangle& source, const Rectangle& destination) const noexcept; private: //============================================================================== int flags; }; #endif // JUCE_RECTANGLEPLACEMENT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/000077500000000000000000000000001320201440200247225ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/application/000077500000000000000000000000001320201440200272255ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp000066400000000000000000000056711320201440200332130ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ JUCEApplication::JUCEApplication() {} JUCEApplication::~JUCEApplication() {} //============================================================================== JUCEApplication* JUCE_CALLTYPE JUCEApplication::getInstance() noexcept { return dynamic_cast (JUCEApplicationBase::getInstance()); } bool JUCEApplication::moreThanOneInstanceAllowed() { return true; } void JUCEApplication::anotherInstanceStarted (const String&) {} void JUCEApplication::suspended() {} void JUCEApplication::resumed() {} void JUCEApplication::systemRequestedQuit() { quit(); } void JUCEApplication::unhandledException (const std::exception*, const String&, int) { jassertfalse; } //============================================================================== ApplicationCommandTarget* JUCEApplication::getNextCommandTarget() { return nullptr; } void JUCEApplication::getAllCommands (Array& commands) { commands.add (StandardApplicationCommandIDs::quit); } void JUCEApplication::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result) { if (commandID == StandardApplicationCommandIDs::quit) { result.setInfo (TRANS("Quit"), TRANS("Quits the application"), "Application", 0); result.defaultKeypresses.add (KeyPress ('q', ModifierKeys::commandModifier, 0)); } } bool JUCEApplication::perform (const InvocationInfo& info) { if (info.commandID == StandardApplicationCommandIDs::quit) { systemRequestedQuit(); return true; } return false; } //============================================================================== #if JUCE_MAC extern void juce_initialiseMacMainMenu(); #endif bool JUCEApplication::initialiseApp() { if (JUCEApplicationBase::initialiseApp()) { #if JUCE_MAC juce_initialiseMacMainMenu(); // (needs to get the app's name) #endif return true; } return false; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.h000066400000000000000000000166341320201440200326610ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_APPLICATION_H_INCLUDED #define JUCE_APPLICATION_H_INCLUDED //============================================================================== /** An instance of this class is used to specify initialisation and shutdown code for the application. Any application that wants to run an event loop must declare a subclass of JUCEApplicationBase or JUCEApplication, and implement its various pure virtual methods. It then needs to use the START_JUCE_APPLICATION macro somewhere in a CPP file to declare an instance of this class and generate suitable platform-specific boilerplate code to launch the app. Note that this class is derived from JUCEApplicationBase, which contains most of the useful methods and functionality. This derived class is here simply as a convenient way to also inherit from an ApplicationCommandTarget, and to implement default versions of some of the pure virtual base class methods. But you can derive your app object directly from JUCEApplicationBase if you want to, and by doing so can avoid having a dependency on the juce_gui_basics module. e.g. @code class MyJUCEApp : public JUCEApplication { public: MyJUCEApp() {} ~MyJUCEApp() {} void initialise (const String& commandLine) override { myMainWindow = new MyApplicationWindow(); myMainWindow->setBounds (100, 100, 400, 500); myMainWindow->setVisible (true); } void shutdown() override { myMainWindow = nullptr; } const String getApplicationName() override { return "Super JUCE-o-matic"; } const String getApplicationVersion() override { return "1.0"; } private: ScopedPointer myMainWindow; }; // this generates boilerplate code to launch our app class: START_JUCE_APPLICATION (MyJUCEApp) @endcode @see JUCEApplicationBase, START_JUCE_APPLICATION */ class JUCE_API JUCEApplication : public JUCEApplicationBase, public ApplicationCommandTarget { public: //============================================================================== /** Constructs a JUCE app object. If subclasses implement a constructor or destructor, they shouldn't call any JUCE code in there - put your startup/shutdown code in initialise() and shutdown() instead. */ JUCEApplication(); /** Destructor. If subclasses implement a constructor or destructor, they shouldn't call any JUCE code in there - put your startup/shutdown code in initialise() and shutdown() instead. */ ~JUCEApplication(); //============================================================================== /** Returns the global instance of the application object being run. */ static JUCEApplication* JUCE_CALLTYPE getInstance() noexcept; //============================================================================== #if DOXYGEN /** Returns the application's name. */ virtual const String getApplicationName() = 0; /** Returns the application's version number. */ virtual const String getApplicationVersion() = 0; #endif /** Checks whether multiple instances of the app are allowed. If you application class returns true for this, more than one instance is permitted to run (except on OSX where the OS automatically stops you launching a second instance of an app without explicitly starting it from the command-line). If it's false, the second instance won't start, but it you will still get a callback to anotherInstanceStarted() to tell you about this - which gives you a chance to react to what the user was trying to do. */ bool moreThanOneInstanceAllowed() override; /** Indicates that the user has tried to start up another instance of the app. This will get called even if moreThanOneInstanceAllowed() is false. */ void anotherInstanceStarted (const String& commandLine) override; /** Called when the operating system is trying to close the application. The default implementation of this method is to call quit(), but it may be overloaded to ignore the request or do some other special behaviour instead. For example, you might want to offer the user the chance to save their changes before quitting, and give them the chance to cancel. If you want to send a quit signal to your app, this is the correct method to call, because it means that requests that come from the system get handled in the same way as those from your own application code. So e.g. you'd call this method from a "quit" item on a menu bar. */ void systemRequestedQuit() override; /** This method is called when the application is being put into background mode by the operating system. */ void suspended() override; /** This method is called when the application is being woken from background mode by the operating system. */ void resumed() override; /** If any unhandled exceptions make it through to the message dispatch loop, this callback will be triggered, in case you want to log them or do some other type of error-handling. If the type of exception is derived from the std::exception class, the pointer passed-in will be valid. If the exception is of unknown type, this pointer will be null. */ void unhandledException (const std::exception* e, const String& sourceFilename, int lineNumber) override; //============================================================================== /** @internal */ ApplicationCommandTarget* getNextCommandTarget() override; /** @internal */ void getCommandInfo (CommandID, ApplicationCommandInfo&) override; /** @internal */ void getAllCommands (Array&) override; /** @internal */ bool perform (const InvocationInfo&) override; private: bool initialiseApp() override; JUCE_DECLARE_NON_COPYABLE (JUCEApplication) }; #endif // JUCE_APPLICATION_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/000077500000000000000000000000001320201440200264205ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp000066400000000000000000000033051320201440200324210ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ArrowButton::ArrowButton (const String& name, float arrowDirectionInRadians, Colour arrowColour) : Button (name), colour (arrowColour) { path.addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, 0.5f, 0.5f)); } ArrowButton::~ArrowButton() {} void ArrowButton::paintButton (Graphics& g, bool /*isMouseOverButton*/, bool isButtonDown) { Path p (path); const float offset = isButtonDown ? 1.0f : 0.0f; p.applyTransform (path.getTransformToScaleToFit (offset, offset, getWidth() - 3.0f, getHeight() - 3.0f, false)); DropShadow (Colours::black.withAlpha (0.3f), isButtonDown ? 2 : 4, Point()).drawForPath (g, p); g.setColour (colour); g.fillPath (p); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.h000066400000000000000000000040451320201440200320700ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_ARROWBUTTON_H_INCLUDED #define JUCE_ARROWBUTTON_H_INCLUDED //============================================================================== /** A button with an arrow in it. @see Button */ class JUCE_API ArrowButton : public Button { public: //============================================================================== /** Creates an ArrowButton. @param buttonName the name to give the button @param arrowDirection the direction the arrow should point in, where 0.0 is pointing right, 0.25 is down, 0.5 is left, 0.75 is up @param arrowColour the colour to use for the arrow */ ArrowButton (const String& buttonName, float arrowDirection, Colour arrowColour); /** Destructor. */ ~ArrowButton(); /** @internal */ void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; private: Colour colour; Path path; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArrowButton) }; #endif // JUCE_ARROWBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp000066400000000000000000000454171320201440200314200ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class Button::CallbackHelper : public Timer, public ApplicationCommandManagerListener, public ValueListener, public KeyListener { public: CallbackHelper (Button& b) : button (b) {} void timerCallback() override { button.repeatTimerCallback(); } bool keyStateChanged (bool, Component*) override { return button.keyStateChangedCallback(); } void valueChanged (Value& value) override { if (value.refersToSameSourceAs (button.isOn)) button.setToggleState (button.isOn.getValue(), sendNotification); } bool keyPressed (const KeyPress&, Component*) override { // returning true will avoid forwarding events for keys that we're using as shortcuts return button.isShortcutPressed(); } void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) override { if (info.commandID == button.commandID && (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) == 0) button.flashButtonState(); } void applicationCommandListChanged() override { button.applicationCommandListChangeCallback(); } private: Button& button; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHelper) }; //============================================================================== Button::Button (const String& name) : Component (name), text (name), buttonPressTime (0), lastRepeatTime (0), commandManagerToUse (nullptr), autoRepeatDelay (-1), autoRepeatSpeed (0), autoRepeatMinimumDelay (-1), radioGroupId (0), connectedEdgeFlags (0), commandID(), buttonState (buttonNormal), lastToggleState (false), clickTogglesState (false), needsToRelease (false), needsRepainting (false), isKeyDown (false), triggerOnMouseDown (false), generateTooltip (false) { callbackHelper = new CallbackHelper (*this); setWantsKeyboardFocus (true); isOn.addListener (callbackHelper); } Button::~Button() { clearShortcuts(); if (commandManagerToUse != nullptr) commandManagerToUse->removeListener (callbackHelper); isOn.removeListener (callbackHelper); callbackHelper = nullptr; } //============================================================================== void Button::setButtonText (const String& newText) { if (text != newText) { text = newText; repaint(); } } void Button::setTooltip (const String& newTooltip) { SettableTooltipClient::setTooltip (newTooltip); generateTooltip = false; } void Button::updateAutomaticTooltip (const ApplicationCommandInfo& info) { if (generateTooltip && commandManagerToUse != nullptr) { String tt (info.description.isNotEmpty() ? info.description : info.shortName); Array keyPresses (commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID)); for (int i = 0; i < keyPresses.size(); ++i) { const String key (keyPresses.getReference(i).getTextDescription()); tt << " ["; if (key.length() == 1) tt << TRANS("shortcut") << ": '" << key << "']"; else tt << key << ']'; } SettableTooltipClient::setTooltip (tt); } } void Button::setConnectedEdges (const int newFlags) { if (connectedEdgeFlags != newFlags) { connectedEdgeFlags = newFlags; repaint(); } } //============================================================================== void Button::setToggleState (const bool shouldBeOn, const NotificationType notification) { if (shouldBeOn != lastToggleState) { WeakReference deletionWatcher (this); if (shouldBeOn) { turnOffOtherButtonsInGroup (notification); if (deletionWatcher == nullptr) return; } if (getToggleState() != shouldBeOn) // this test means that if the value is void rather than explicitly set to isOn = shouldBeOn; // false, it won't be changed unless the required value is true. lastToggleState = shouldBeOn; repaint(); if (notification != dontSendNotification) { // async callbacks aren't possible here jassert (notification != sendNotificationAsync); sendClickMessage (ModifierKeys::getCurrentModifiers()); if (deletionWatcher == nullptr) return; } if (notification != dontSendNotification) sendStateMessage(); else buttonStateChanged(); } } void Button::setToggleState (const bool shouldBeOn, bool sendChange) { setToggleState (shouldBeOn, sendChange ? sendNotification : dontSendNotification); } void Button::setClickingTogglesState (const bool shouldToggle) noexcept { clickTogglesState = shouldToggle; // if you've got clickTogglesState turned on, you shouldn't also connect the button // up to be a command invoker. Instead, your command handler must flip the state of whatever // it is that this button represents, and the button will update its state to reflect this // in the applicationCommandListChanged() method. jassert (commandManagerToUse == nullptr || ! clickTogglesState); } bool Button::getClickingTogglesState() const noexcept { return clickTogglesState; } void Button::setRadioGroupId (const int newGroupId, NotificationType notification) { if (radioGroupId != newGroupId) { radioGroupId = newGroupId; if (lastToggleState) turnOffOtherButtonsInGroup (notification); } } void Button::turnOffOtherButtonsInGroup (const NotificationType notification) { if (Component* const p = getParentComponent()) { if (radioGroupId != 0) { WeakReference deletionWatcher (this); for (int i = p->getNumChildComponents(); --i >= 0;) { Component* const c = p->getChildComponent (i); if (c != this) { if (Button* const b = dynamic_cast (c)) { if (b->getRadioGroupId() == radioGroupId) { b->setToggleState (false, notification); if (deletionWatcher == nullptr) return; } } } } } } } //============================================================================== void Button::enablementChanged() { updateState(); repaint(); } Button::ButtonState Button::updateState() { return updateState (isMouseOver (true), isMouseButtonDown()); } Button::ButtonState Button::updateState (const bool over, const bool down) { ButtonState newState = buttonNormal; if (isEnabled() && isVisible() && ! isCurrentlyBlockedByAnotherModalComponent()) { if ((down && (over || (triggerOnMouseDown && buttonState == buttonDown))) || isKeyDown) newState = buttonDown; else if (over) newState = buttonOver; } setState (newState); return newState; } void Button::setState (const ButtonState newState) { if (buttonState != newState) { buttonState = newState; repaint(); if (buttonState == buttonDown) { buttonPressTime = Time::getApproximateMillisecondCounter(); lastRepeatTime = 0; } sendStateMessage(); } } bool Button::isDown() const noexcept { return buttonState == buttonDown; } bool Button::isOver() const noexcept { return buttonState != buttonNormal; } void Button::buttonStateChanged() {} uint32 Button::getMillisecondsSinceButtonDown() const noexcept { const uint32 now = Time::getApproximateMillisecondCounter(); return now > buttonPressTime ? now - buttonPressTime : 0; } void Button::setTriggeredOnMouseDown (const bool isTriggeredOnMouseDown) noexcept { triggerOnMouseDown = isTriggeredOnMouseDown; } //============================================================================== void Button::clicked() { } void Button::clicked (const ModifierKeys&) { clicked(); } enum { clickMessageId = 0x2f3f4f99 }; void Button::triggerClick() { postCommandMessage (clickMessageId); } void Button::internalClickCallback (const ModifierKeys& modifiers) { if (clickTogglesState) { const bool shouldBeOn = (radioGroupId != 0 || ! lastToggleState); if (shouldBeOn != getToggleState()) { setToggleState (shouldBeOn, sendNotification); return; } } sendClickMessage (modifiers); } void Button::flashButtonState() { if (isEnabled()) { needsToRelease = true; setState (buttonDown); callbackHelper->startTimer (100); } } void Button::handleCommandMessage (int commandId) { if (commandId == clickMessageId) { if (isEnabled()) { flashButtonState(); internalClickCallback (ModifierKeys::getCurrentModifiers()); } } else { Component::handleCommandMessage (commandId); } } //============================================================================== void Button::addListener (ButtonListener* const newListener) { buttonListeners.add (newListener); } void Button::removeListener (ButtonListener* const listener) { buttonListeners.remove (listener); } void Button::sendClickMessage (const ModifierKeys& modifiers) { Component::BailOutChecker checker (this); if (commandManagerToUse != nullptr && commandID != 0) { ApplicationCommandTarget::InvocationInfo info (commandID); info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromButton; info.originatingComponent = this; commandManagerToUse->invoke (info, true); } clicked (modifiers); if (! checker.shouldBailOut()) buttonListeners.callChecked (checker, &ButtonListener::buttonClicked, this); // (can't use Button::Listener due to idiotic VC2005 bug) } void Button::sendStateMessage() { Component::BailOutChecker checker (this); buttonStateChanged(); if (! checker.shouldBailOut()) buttonListeners.callChecked (checker, &ButtonListener::buttonStateChanged, this); } //============================================================================== void Button::paint (Graphics& g) { if (needsToRelease && isEnabled()) { needsToRelease = false; needsRepainting = true; } paintButton (g, isOver(), isDown()); } //============================================================================== void Button::mouseEnter (const MouseEvent&) { updateState (true, false); } void Button::mouseExit (const MouseEvent&) { updateState (false, false); } void Button::mouseDown (const MouseEvent& e) { updateState (true, true); if (isDown()) { if (autoRepeatDelay >= 0) callbackHelper->startTimer (autoRepeatDelay); if (triggerOnMouseDown) internalClickCallback (e.mods); } } void Button::mouseUp (const MouseEvent& e) { const bool wasDown = isDown(); const bool wasOver = isOver(); updateState (isMouseOver(), false); if (wasDown && wasOver && ! triggerOnMouseDown) internalClickCallback (e.mods); } void Button::mouseDrag (const MouseEvent&) { const ButtonState oldState = buttonState; updateState (isMouseOver(), true); if (autoRepeatDelay >= 0 && buttonState != oldState && isDown()) callbackHelper->startTimer (autoRepeatSpeed); } void Button::focusGained (FocusChangeType) { updateState(); repaint(); } void Button::focusLost (FocusChangeType) { updateState(); repaint(); } void Button::visibilityChanged() { needsToRelease = false; updateState(); } void Button::parentHierarchyChanged() { Component* const newKeySource = (shortcuts.size() == 0) ? nullptr : getTopLevelComponent(); if (newKeySource != keySource.get()) { if (keySource != nullptr) keySource->removeKeyListener (callbackHelper); keySource = newKeySource; if (keySource != nullptr) keySource->addKeyListener (callbackHelper); } } //============================================================================== void Button::setCommandToTrigger (ApplicationCommandManager* const newCommandManager, const CommandID newCommandID, const bool generateTip) { commandID = newCommandID; generateTooltip = generateTip; if (commandManagerToUse != newCommandManager) { if (commandManagerToUse != nullptr) commandManagerToUse->removeListener (callbackHelper); commandManagerToUse = newCommandManager; if (commandManagerToUse != nullptr) commandManagerToUse->addListener (callbackHelper); // if you've got clickTogglesState turned on, you shouldn't also connect the button // up to be a command invoker. Instead, your command handler must flip the state of whatever // it is that this button represents, and the button will update its state to reflect this // in the applicationCommandListChanged() method. jassert (commandManagerToUse == nullptr || ! clickTogglesState); } if (commandManagerToUse != nullptr) applicationCommandListChangeCallback(); else setEnabled (true); } void Button::applicationCommandListChangeCallback() { if (commandManagerToUse != nullptr) { ApplicationCommandInfo info (0); if (commandManagerToUse->getTargetForCommand (commandID, info) != nullptr) { updateAutomaticTooltip (info); setEnabled ((info.flags & ApplicationCommandInfo::isDisabled) == 0); setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, dontSendNotification); } else { setEnabled (false); } } } //============================================================================== void Button::addShortcut (const KeyPress& key) { if (key.isValid()) { jassert (! isRegisteredForShortcut (key)); // already registered! shortcuts.add (key); parentHierarchyChanged(); } } void Button::clearShortcuts() { shortcuts.clear(); parentHierarchyChanged(); } bool Button::isShortcutPressed() const { if (isShowing() && ! isCurrentlyBlockedByAnotherModalComponent()) for (int i = shortcuts.size(); --i >= 0;) if (shortcuts.getReference(i).isCurrentlyDown()) return true; return false; } bool Button::isRegisteredForShortcut (const KeyPress& key) const { for (int i = shortcuts.size(); --i >= 0;) if (key == shortcuts.getReference(i)) return true; return false; } bool Button::keyStateChangedCallback() { if (! isEnabled()) return false; const bool wasDown = isKeyDown; isKeyDown = isShortcutPressed(); if (autoRepeatDelay >= 0 && (isKeyDown && ! wasDown)) callbackHelper->startTimer (autoRepeatDelay); updateState(); if (isEnabled() && wasDown && ! isKeyDown) { internalClickCallback (ModifierKeys::getCurrentModifiers()); // (return immediately - this button may now have been deleted) return true; } return wasDown || isKeyDown; } bool Button::keyPressed (const KeyPress& key) { if (isEnabled() && key.isKeyCode (KeyPress::returnKey)) { triggerClick(); return true; } return false; } //============================================================================== void Button::setRepeatSpeed (const int initialDelayMillisecs, const int repeatMillisecs, const int minimumDelayInMillisecs) noexcept { autoRepeatDelay = initialDelayMillisecs; autoRepeatSpeed = repeatMillisecs; autoRepeatMinimumDelay = jmin (autoRepeatSpeed, minimumDelayInMillisecs); } void Button::repeatTimerCallback() { if (needsRepainting) { callbackHelper->stopTimer(); updateState(); needsRepainting = false; } else if (autoRepeatSpeed > 0 && (isKeyDown || (updateState() == buttonDown))) { int repeatSpeed = autoRepeatSpeed; if (autoRepeatMinimumDelay >= 0) { double timeHeldDown = jmin (1.0, getMillisecondsSinceButtonDown() / 4000.0); timeHeldDown *= timeHeldDown; repeatSpeed = repeatSpeed + (int) (timeHeldDown * (autoRepeatMinimumDelay - repeatSpeed)); } repeatSpeed = jmax (1, repeatSpeed); const uint32 now = Time::getMillisecondCounter(); // if we've been blocked from repeating often enough, speed up the repeat timer to compensate.. if (lastRepeatTime != 0 && (int) (now - lastRepeatTime) > repeatSpeed * 2) repeatSpeed = jmax (1, repeatSpeed / 2); lastRepeatTime = now; callbackHelper->startTimer (repeatSpeed); internalClickCallback (ModifierKeys::getCurrentModifiers()); } else if (! needsToRelease) { callbackHelper->stopTimer(); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h000066400000000000000000000507461320201440200310660ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_BUTTON_H_INCLUDED #define JUCE_BUTTON_H_INCLUDED //============================================================================== /** A base class for buttons. This contains all the logic for button behaviours such as enabling/disabling, responding to shortcut keystrokes, auto-repeating when held down, toggle-buttons and radio groups, etc. @see TextButton, DrawableButton, ToggleButton */ class JUCE_API Button : public Component, public SettableTooltipClient { protected: //============================================================================== /** Creates a button. @param buttonName the text to put in the button (the component's name is also initially set to this string, but these can be changed later using the setName() and setButtonText() methods) */ explicit Button (const String& buttonName); public: /** Destructor. */ virtual ~Button(); //============================================================================== /** Changes the button's text. @see getButtonText */ void setButtonText (const String& newText); /** Returns the text displayed in the button. @see setButtonText */ const String& getButtonText() const { return text; } //============================================================================== /** Returns true if the button is currently being held down. @see isOver */ bool isDown() const noexcept; /** Returns true if the mouse is currently over the button. This will be also be true if the button is being held down. @see isDown */ bool isOver() const noexcept; //============================================================================== /** A button has an on/off state associated with it, and this changes that. By default buttons are 'off' and for simple buttons that you click to perform an action you won't change this. Toggle buttons, however will want to change their state when turned on or off. @param shouldBeOn whether to set the button's toggle state to be on or off. If it's a member of a button group, this will always try to turn it on, and to turn off any other buttons in the group @param notification determines the behaviour if the value changes - this can invoke a synchronous call to clicked(), but sendNotificationAsync is not supported @see getToggleState, setRadioGroupId */ void setToggleState (bool shouldBeOn, NotificationType notification); /** Returns true if the button is 'on'. By default buttons are 'off' and for simple buttons that you click to perform an action you won't change this. Toggle buttons, however will want to change their state when turned on or off. @see setToggleState */ bool getToggleState() const noexcept { return isOn.getValue(); } /** Returns the Value object that represents the botton's toggle state. You can use this Value object to connect the button's state to external values or setters, either by taking a copy of the Value, or by using Value::referTo() to make it point to your own Value object. @see getToggleState, Value */ Value& getToggleStateValue() noexcept { return isOn; } /** This tells the button to automatically flip the toggle state when the button is clicked. If set to true, then before the clicked() callback occurs, the toggle-state of the button is flipped. */ void setClickingTogglesState (bool shouldAutoToggleOnClick) noexcept; /** Returns true if this button is set to be an automatic toggle-button. This returns the last value that was passed to setClickingTogglesState(). */ bool getClickingTogglesState() const noexcept; //============================================================================== /** Enables the button to act as a member of a mutually-exclusive group of 'radio buttons'. If the group ID is set to a non-zero number, then this button will act as part of a group of buttons with the same ID, only one of which can be 'on' at the same time. Note that when it's part of a group, clicking a toggle-button that's 'on' won't turn it off. To find other buttons with the same ID, this button will search through its sibling components for ToggleButtons, so all the buttons for a particular group must be placed inside the same parent component. Set the group ID back to zero if you want it to act as a normal toggle button again. The notification argument lets you specify how other buttons should react to being turned on or off in response to this call. @see getRadioGroupId */ void setRadioGroupId (int newGroupId, NotificationType notification = sendNotification); /** Returns the ID of the group to which this button belongs. (See setRadioGroupId() for an explanation of this). */ int getRadioGroupId() const noexcept { return radioGroupId; } //============================================================================== /** Used to receive callbacks when a button is clicked. @see Button::addListener, Button::removeListener */ class JUCE_API Listener { public: /** Destructor. */ virtual ~Listener() {} /** Called when the button is clicked. */ virtual void buttonClicked (Button*) = 0; /** Called when the button's state changes. */ virtual void buttonStateChanged (Button*) {} }; /** Registers a listener to receive events when this button's state changes. If the listener is already registered, this will not register it again. @see removeListener */ void addListener (Listener* newListener); /** Removes a previously-registered button listener @see addListener */ void removeListener (Listener* listener); //============================================================================== /** Causes the button to act as if it's been clicked. This will asynchronously make the button draw itself going down and up, and will then call back the clicked() method as if mouse was clicked on it. @see clicked */ virtual void triggerClick(); //============================================================================== /** Sets a command ID for this button to automatically invoke when it's clicked. When the button is pressed, it will use the given manager to trigger the command ID. Obviously be careful that the ApplicationCommandManager doesn't get deleted before this button is. To disable the command triggering, call this method and pass nullptr as the command manager. If generateTooltip is true, then the button's tooltip will be automatically generated based on the name of this command and its current shortcut key. @see addShortcut, getCommandID */ void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse, CommandID commandID, bool generateTooltip); /** Returns the command ID that was set by setCommandToTrigger(). */ CommandID getCommandID() const noexcept { return commandID; } //============================================================================== /** Assigns a shortcut key to trigger the button. The button registers itself with its top-level parent component for keypresses. Note that a different way of linking buttons to keypresses is by using the setCommandToTrigger() method to invoke a command. @see clearShortcuts */ void addShortcut (const KeyPress&); /** Removes all key shortcuts that had been set for this button. @see addShortcut */ void clearShortcuts(); /** Returns true if the given keypress is a shortcut for this button. @see addShortcut */ bool isRegisteredForShortcut (const KeyPress&) const; //============================================================================== /** Sets an auto-repeat speed for the button when it is held down. (Auto-repeat is disabled by default). @param initialDelayInMillisecs how long to wait after the mouse is pressed before triggering the next click. If this is zero, auto-repeat is disabled @param repeatDelayInMillisecs the frequently subsequent repeated clicks should be triggered @param minimumDelayInMillisecs if this is greater than 0, the auto-repeat speed will get faster, the longer the button is held down, up to the minimum interval specified here */ void setRepeatSpeed (int initialDelayInMillisecs, int repeatDelayInMillisecs, int minimumDelayInMillisecs = -1) noexcept; /** Sets whether the button click should happen when the mouse is pressed or released. By default the button is only considered to have been clicked when the mouse is released, but setting this to true will make it call the clicked() method as soon as the button is pressed. This is useful if the button is being used to show a pop-up menu, as it allows the click to be used as a drag onto the menu. */ void setTriggeredOnMouseDown (bool isTriggeredOnMouseDown) noexcept; /** Returns the number of milliseconds since the last time the button went into the 'down' state. */ uint32 getMillisecondsSinceButtonDown() const noexcept; //============================================================================== /** Sets the tooltip for this button. @see TooltipClient, TooltipWindow */ void setTooltip (const String& newTooltip) override; //============================================================================== /** A combination of these flags are used by setConnectedEdges(). */ enum ConnectedEdgeFlags { ConnectedOnLeft = 1, ConnectedOnRight = 2, ConnectedOnTop = 4, ConnectedOnBottom = 8 }; /** Hints about which edges of the button might be connected to adjoining buttons. The value passed in is a bitwise combination of any of the values in the ConnectedEdgeFlags enum. E.g. if you are placing two buttons adjacent to each other, you could use this to indicate which edges are touching, and the LookAndFeel might choose to drawn them without rounded corners on the edges that connect. It's only a hint, so the LookAndFeel can choose to ignore it if it's not relevent for this type of button. */ void setConnectedEdges (int connectedEdgeFlags); /** Returns the set of flags passed into setConnectedEdges(). */ int getConnectedEdgeFlags() const noexcept { return connectedEdgeFlags; } /** Indicates whether the button adjoins another one on its left edge. @see setConnectedEdges */ bool isConnectedOnLeft() const noexcept { return (connectedEdgeFlags & ConnectedOnLeft) != 0; } /** Indicates whether the button adjoins another one on its right edge. @see setConnectedEdges */ bool isConnectedOnRight() const noexcept { return (connectedEdgeFlags & ConnectedOnRight) != 0; } /** Indicates whether the button adjoins another one on its top edge. @see setConnectedEdges */ bool isConnectedOnTop() const noexcept { return (connectedEdgeFlags & ConnectedOnTop) != 0; } /** Indicates whether the button adjoins another one on its bottom edge. @see setConnectedEdges */ bool isConnectedOnBottom() const noexcept { return (connectedEdgeFlags & ConnectedOnBottom) != 0; } //============================================================================== /** Used by setState(). */ enum ButtonState { buttonNormal, buttonOver, buttonDown }; /** Can be used to force the button into a particular state. This only changes the button's appearance, it won't trigger a click, or stop any mouse-clicks from happening. The state that you set here will only last until it is automatically changed when the mouse enters or exits the button, or the mouse-button is pressed or released. */ void setState (ButtonState newState); /** Returns the button's current over/down/up state. */ ButtonState getState() const noexcept { return buttonState; } // This method's parameters have changed - see the new version. JUCE_DEPRECATED (void setToggleState (bool, bool)); //============================================================================== /** This abstract base class is implemented by LookAndFeel classes to provide button-drawing functionality. */ struct JUCE_API LookAndFeelMethods { virtual ~LookAndFeelMethods() {} virtual void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour, bool isMouseOverButton, bool isButtonDown) = 0; virtual Font getTextButtonFont (TextButton&, int buttonHeight) = 0; virtual int getTextButtonWidthToFitText (TextButton&, int buttonHeight) = 0; /** Draws the text for a TextButton. */ virtual void drawButtonText (Graphics&, TextButton&, bool isMouseOverButton, bool isButtonDown) = 0; /** Draws the contents of a standard ToggleButton. */ virtual void drawToggleButton (Graphics&, ToggleButton&, bool isMouseOverButton, bool isButtonDown) = 0; virtual void changeToggleButtonWidthToFitText (ToggleButton&) = 0; virtual void drawTickBox (Graphics&, Component&, float x, float y, float w, float h, bool ticked, bool isEnabled, bool isMouseOverButton, bool isButtonDown) = 0; virtual void drawDrawableButton (Graphics&, DrawableButton&, bool isMouseOverButton, bool isButtonDown) = 0; private: #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // These method have been deprecated: see their replacements above. virtual int getTextButtonFont (TextButton&) { return 0; } virtual int changeTextButtonWidthToFitText (TextButton&, int) { return 0; } #endif }; protected: //============================================================================== /** This method is called when the button has been clicked. Subclasses can override this to perform whatever they actions they need to do. Alternatively, a ButtonListener can be added to the button, and these listeners will be called when the click occurs. @see triggerClick */ virtual void clicked(); /** This method is called when the button has been clicked. By default it just calls clicked(), but you might want to override it to handle things like clicking when a modifier key is pressed, etc. @see ModifierKeys */ virtual void clicked (const ModifierKeys& modifiers); /** Subclasses should override this to actually paint the button's contents. It's better to use this than the paint method, because it gives you information about the over/down state of the button. @param g the graphics context to use @param isMouseOverButton true if the button is either in the 'over' or 'down' state @param isButtonDown true if the button should be drawn in the 'down' position */ virtual void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) = 0; /** Called when the button's up/down/over state changes. Subclasses can override this if they need to do something special when the button goes up or down. @see isDown, isOver */ virtual void buttonStateChanged(); //============================================================================== /** @internal */ virtual void internalClickCallback (const ModifierKeys&); /** @internal */ void handleCommandMessage (int commandId) override; /** @internal */ void mouseEnter (const MouseEvent&) override; /** @internal */ void mouseExit (const MouseEvent&) override; /** @internal */ void mouseDown (const MouseEvent&) override; /** @internal */ void mouseDrag (const MouseEvent&) override; /** @internal */ void mouseUp (const MouseEvent&) override; /** @internal */ bool keyPressed (const KeyPress&) override; /** @internal */ using Component::keyStateChanged; /** @internal */ void paint (Graphics&) override; /** @internal */ void parentHierarchyChanged() override; /** @internal */ void visibilityChanged() override; /** @internal */ void focusGained (FocusChangeType) override; /** @internal */ void focusLost (FocusChangeType) override; /** @internal */ void enablementChanged() override; private: //============================================================================== Array shortcuts; WeakReference keySource; String text; ListenerList buttonListeners; class CallbackHelper; friend class CallbackHelper; friend struct ContainerDeletePolicy; ScopedPointer callbackHelper; uint32 buttonPressTime, lastRepeatTime; ApplicationCommandManager* commandManagerToUse; int autoRepeatDelay, autoRepeatSpeed, autoRepeatMinimumDelay; int radioGroupId, connectedEdgeFlags; CommandID commandID; ButtonState buttonState; Value isOn; bool lastToggleState; bool clickTogglesState; bool needsToRelease; bool needsRepainting; bool isKeyDown; bool triggerOnMouseDown; bool generateTooltip; void repeatTimerCallback(); bool keyStateChangedCallback(); void applicationCommandListChangeCallback(); void updateAutomaticTooltip (const ApplicationCommandInfo&); ButtonState updateState(); ButtonState updateState (bool isOver, bool isDown); bool isShortcutPressed() const; void turnOffOtherButtonsInGroup (NotificationType); void flashButtonState(); void sendClickMessage (const ModifierKeys&); void sendStateMessage(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Button) }; #ifndef DOXYGEN /** This typedef is just for compatibility with old code and VC6 - newer code should use Button::Listener instead. */ typedef Button::Listener ButtonListener; #endif #endif // JUCE_BUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp000066400000000000000000000151531320201440200330540ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DrawableButton::DrawableButton (const String& name, const DrawableButton::ButtonStyle buttonStyle) : Button (name), style (buttonStyle), currentImage (nullptr), edgeIndent (3) { } DrawableButton::~DrawableButton() { } //============================================================================== static Drawable* copyDrawableIfNotNull (const Drawable* const d) { return d != nullptr ? d->createCopy() : nullptr; } void DrawableButton::setImages (const Drawable* normal, const Drawable* over, const Drawable* down, const Drawable* disabled, const Drawable* normalOn, const Drawable* overOn, const Drawable* downOn, const Drawable* disabledOn) { jassert (normal != nullptr); // you really need to give it at least a normal image.. normalImage = copyDrawableIfNotNull (normal); overImage = copyDrawableIfNotNull (over); downImage = copyDrawableIfNotNull (down); disabledImage = copyDrawableIfNotNull (disabled); normalImageOn = copyDrawableIfNotNull (normalOn); overImageOn = copyDrawableIfNotNull (overOn); downImageOn = copyDrawableIfNotNull (downOn); disabledImageOn = copyDrawableIfNotNull (disabledOn); currentImage = nullptr; buttonStateChanged(); } //============================================================================== void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle) { if (style != newStyle) { style = newStyle; buttonStateChanged(); } } void DrawableButton::setEdgeIndent (const int numPixelsIndent) { edgeIndent = numPixelsIndent; repaint(); resized(); } Rectangle DrawableButton::getImageBounds() const { Rectangle r (getLocalBounds()); if (style != ImageStretched) { int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); if (style == ImageOnButtonBackground) { indentX = jmax (getWidth() / 4, indentX); indentY = jmax (getHeight() / 4, indentY); } else if (style == ImageAboveTextLabel) { r = r.withTrimmedBottom (jmin (16, proportionOfHeight (0.25f))); } r = r.reduced (indentX, indentY); } return r.toFloat(); } void DrawableButton::resized() { Button::resized(); if (currentImage != nullptr) { if (style == ImageRaw) currentImage->setOriginWithOriginalSize (Point()); else currentImage->setTransformToFit (getImageBounds(), style == ImageStretched ? RectanglePlacement::stretchToFit : RectanglePlacement::centred); } } void DrawableButton::buttonStateChanged() { repaint(); Drawable* imageToDraw = nullptr; float opacity = 1.0f; if (isEnabled()) { imageToDraw = getCurrentImage(); } else { imageToDraw = getToggleState() ? disabledImageOn : disabledImage; if (imageToDraw == nullptr) { opacity = 0.4f; imageToDraw = getNormalImage(); } } if (imageToDraw != currentImage) { removeChildComponent (currentImage); currentImage = imageToDraw; if (currentImage != nullptr) { currentImage->setInterceptsMouseClicks (false, false); addAndMakeVisible (currentImage); resized(); } } if (currentImage != nullptr) currentImage->setAlpha (opacity); } void DrawableButton::enablementChanged() { Button::enablementChanged(); buttonStateChanged(); } void DrawableButton::colourChanged() { repaint(); } void DrawableButton::paintButton (Graphics& g, const bool isMouseOverButton, const bool isButtonDown) { LookAndFeel& lf = getLookAndFeel(); if (style == ImageOnButtonBackground) lf.drawButtonBackground (g, *this, findColour (getToggleState() ? TextButton::buttonOnColourId : TextButton::buttonColourId), isMouseOverButton, isButtonDown); else lf.drawDrawableButton (g, *this, isMouseOverButton, isButtonDown); } //============================================================================== Drawable* DrawableButton::getCurrentImage() const noexcept { if (isDown()) return getDownImage(); if (isOver()) return getOverImage(); return getNormalImage(); } Drawable* DrawableButton::getNormalImage() const noexcept { return (getToggleState() && normalImageOn != nullptr) ? normalImageOn : normalImage; } Drawable* DrawableButton::getOverImage() const noexcept { if (getToggleState()) { if (overImageOn != nullptr) return overImageOn; if (normalImageOn != nullptr) return normalImageOn; } return overImage != nullptr ? overImage : normalImage; } Drawable* DrawableButton::getDownImage() const noexcept { if (Drawable* const d = getToggleState() ? downImageOn : downImage) return d; return getOverImage(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.h000066400000000000000000000231261320201440200325200ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLEBUTTON_H_INCLUDED #define JUCE_DRAWABLEBUTTON_H_INCLUDED //============================================================================== /** A button that displays a Drawable. Up to three Drawable objects can be given to this button, to represent the 'normal', 'over' and 'down' states. @see Button */ class JUCE_API DrawableButton : public Button { public: //============================================================================== enum ButtonStyle { ImageFitted, /**< The button will just display the images, but will resize and centre them to fit inside it. */ ImageRaw, /**< The button will just display the images in their normal size and position. This leaves it up to the caller to make sure the images are the correct size and position for the button. */ ImageAboveTextLabel, /**< Draws the button as a text label across the bottom with the image resized and scaled to fit above it. */ ImageOnButtonBackground, /**< Draws the button as a standard rounded-rectangle button with the image on top. Note that if you use this style, the colour IDs that control the button colour are TextButton::buttonColourId and TextButton::buttonOnColourId. */ ImageStretched /**< Fills the button with a stretched version of the image. */ }; //============================================================================== /** Creates a DrawableButton. After creating one of these, use setImages() to specify the drawables to use. @param buttonName the name to give the component @param buttonStyle the layout to use @see ButtonStyle, setButtonStyle, setImages */ DrawableButton (const String& buttonName, ButtonStyle buttonStyle); /** Destructor. */ ~DrawableButton(); //============================================================================== /** Sets up the images to draw for the various button states. The button will keep its own internal copies of these drawables. @param normalImage the thing to draw for the button's 'normal' state. An internal copy will be made of the object passed-in if it is non-zero. @param overImage the thing to draw for the button's 'over' state - if this is zero, the button's normal image will be used when the mouse is over it. An internal copy will be made of the object passed-in if it is non-zero. @param downImage the thing to draw for the button's 'down' state - if this is zero, the 'over' image will be used instead (or the normal image as a last resort). An internal copy will be made of the object passed-in if it is non-zero. @param disabledImage an image to draw when the button is disabled. If this is zero, the normal image will be drawn with a reduced opacity instead. An internal copy will be made of the object passed-in if it is non-zero. @param normalImageOn same as the normalImage, but this is used when the button's toggle state is 'on'. If this is nullptr, the normal image is used instead @param overImageOn same as the overImage, but this is used when the button's toggle state is 'on'. If this is nullptr, the normalImageOn is drawn instead @param downImageOn same as the downImage, but this is used when the button's toggle state is 'on'. If this is nullptr, the overImageOn is drawn instead @param disabledImageOn same as the disabledImage, but this is used when the button's toggle state is 'on'. If this is nullptr, the normal image will be drawn instead with a reduced opacity */ void setImages (const Drawable* normalImage, const Drawable* overImage = nullptr, const Drawable* downImage = nullptr, const Drawable* disabledImage = nullptr, const Drawable* normalImageOn = nullptr, const Drawable* overImageOn = nullptr, const Drawable* downImageOn = nullptr, const Drawable* disabledImageOn = nullptr); //============================================================================== /** Changes the button's style. @see ButtonStyle */ void setButtonStyle (ButtonStyle newStyle); /** Returns the current style. */ ButtonStyle getStyle() const noexcept { return style; } //============================================================================== /** Gives the button an optional amount of space around the edge of the drawable. By default there's a gap of about 3 pixels. */ void setEdgeIndent (int numPixelsIndent); //============================================================================== /** Returns the image that the button is currently displaying. */ Drawable* getCurrentImage() const noexcept; /** Returns the image that the button will use for its normal state. */ Drawable* getNormalImage() const noexcept; /** Returns the image that the button will use when the mouse is over it. */ Drawable* getOverImage() const noexcept; /** Returns the image that the button will use when the mouse is held down on it. */ Drawable* getDownImage() const noexcept; /** Can be overridden to specify a custom position for the image within the button. */ virtual Rectangle getImageBounds() const; //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the link. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. Note that when the ImageOnButtonBackground style is used, the colour IDs that control the button colour are TextButton::buttonColourId and TextButton::buttonOnColourId. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds { textColourId = 0x1004010, /**< The colour to use for the button's text label. */ textColourOnId = 0x1004013, /**< The colour to use for the button's text.when the button's toggle state is "on". */ backgroundColourId = 0x1004011, /**< The colour used to fill the button's background (when the button is toggled 'off'). Note that if you use the ImageOnButtonBackground style, you should use TextButton::buttonColourId to change the button's colour. */ backgroundOnColourId = 0x1004012, /**< The colour used to fill the button's background (when the button is toggled 'on'). Note that if you use the ImageOnButtonBackground style, you should use TextButton::buttonOnColourId to change the button's colour. */ }; //============================================================================== /** @internal */ void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; /** @internal */ void buttonStateChanged() override; /** @internal */ void resized() override; /** @internal */ void enablementChanged() override; /** @internal */ void colourChanged() override; private: //============================================================================== ButtonStyle style; ScopedPointer normalImage, overImage, downImage, disabledImage, normalImageOn, overImageOn, downImageOn, disabledImageOn; Drawable* currentImage; int edgeIndent; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableButton) }; #endif // JUCE_DRAWABLEBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp000066400000000000000000000064321320201440200333000ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ HyperlinkButton::HyperlinkButton (const String& linkText, const URL& linkURL) : Button (linkText), url (linkURL), font (14.0f, Font::underlined), resizeFont (true), justification (Justification::centred) { setMouseCursor (MouseCursor::PointingHandCursor); setTooltip (linkURL.toString (false)); } HyperlinkButton::HyperlinkButton () : Button (String::empty), font (14.0f, Font::underlined), resizeFont (true), justification (Justification::centred) { setMouseCursor (MouseCursor::PointingHandCursor); } HyperlinkButton::~HyperlinkButton() { } //============================================================================== void HyperlinkButton::setFont (const Font& newFont, const bool resizeToMatchComponentHeight, Justification justificationType) { font = newFont; resizeFont = resizeToMatchComponentHeight; justification = justificationType; repaint(); } void HyperlinkButton::setURL (const URL& newURL) noexcept { url = newURL; setTooltip (newURL.toString (false)); } Font HyperlinkButton::getFontToUse() const { if (resizeFont) return font.withHeight (getHeight() * 0.7f); return font; } void HyperlinkButton::changeWidthToFitText() { setSize (getFontToUse().getStringWidth (getButtonText()) + 6, getHeight()); } void HyperlinkButton::colourChanged() { repaint(); } //============================================================================== void HyperlinkButton::clicked() { if (url.isWellFormed()) url.launchInDefaultBrowser(); } void HyperlinkButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { const Colour textColour (findColour (textColourId)); if (isEnabled()) g.setColour ((isMouseOverButton) ? textColour.darker ((isButtonDown) ? 1.3f : 0.4f) : textColour); else g.setColour (textColour.withMultipliedAlpha (0.4f)); g.setFont (getFontToUse()); g.drawText (getButtonText(), getLocalBounds().reduced (1, 0), justification.getOnlyHorizontalFlags() | Justification::verticallyCentred, true); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h000066400000000000000000000101401320201440200327340ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_HYPERLINKBUTTON_H_INCLUDED #define JUCE_HYPERLINKBUTTON_H_INCLUDED //============================================================================== /** A button showing an underlined weblink, that will launch the link when it's clicked. @see Button */ class JUCE_API HyperlinkButton : public Button { public: //============================================================================== /** Creates a HyperlinkButton. @param linkText the text that will be displayed in the button - this is also set as the Component's name, but the text can be changed later with the Button::getButtonText() method @param linkURL the URL to launch when the user clicks the button */ HyperlinkButton (const String& linkText, const URL& linkURL); /** Creates a HyperlinkButton. */ HyperlinkButton(); /** Destructor. */ ~HyperlinkButton(); //============================================================================== /** Changes the font to use for the text. If resizeToMatchComponentHeight is true, the font's height will be adjusted to match the size of the component. */ void setFont (const Font& newFont, bool resizeToMatchComponentHeight, Justification justificationType = Justification::horizontallyCentred); //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the link. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds { textColourId = 0x1001f00, /**< The colour to use for the URL text. */ }; //============================================================================== /** Changes the URL that the button will trigger. */ void setURL (const URL& newURL) noexcept; /** Returns the URL that the button will trigger. */ const URL& getURL() const noexcept { return url; } //============================================================================== /** Resizes the button horizontally to fit snugly around the text. This won't affect the button's height. */ void changeWidthToFitText(); protected: //============================================================================== /** @internal */ void clicked() override; /** @internal */ void colourChanged() override; /** @internal */ void paintButton (Graphics&, bool isMouseOver, bool isButtonDown) override; private: //============================================================================== URL url; Font font; bool resizeFont; Justification justification; Font getFontToUse() const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HyperlinkButton) }; #endif // JUCE_HYPERLINKBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.cpp000066400000000000000000000136401320201440200323540ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ImageButton::ImageButton (const String& text_) : Button (text_), scaleImageToFit (true), preserveProportions (true), alphaThreshold (0) { } ImageButton::~ImageButton() { } void ImageButton::setImages (const bool resizeButtonNowToFitThisImage, const bool rescaleImagesWhenButtonSizeChanges, const bool preserveImageProportions, const Image& normalImage_, const float imageOpacityWhenNormal, Colour overlayColourWhenNormal, const Image& overImage_, const float imageOpacityWhenOver, Colour overlayColourWhenOver, const Image& downImage_, const float imageOpacityWhenDown, Colour overlayColourWhenDown, const float hitTestAlphaThreshold) { normalImage = normalImage_; overImage = overImage_; downImage = downImage_; if (resizeButtonNowToFitThisImage && normalImage.isValid()) { imageBounds.setSize (normalImage.getWidth(), normalImage.getHeight()); setSize (imageBounds.getWidth(), imageBounds.getHeight()); } scaleImageToFit = rescaleImagesWhenButtonSizeChanges; preserveProportions = preserveImageProportions; normalOpacity = imageOpacityWhenNormal; normalOverlay = overlayColourWhenNormal; overOpacity = imageOpacityWhenOver; overOverlay = overlayColourWhenOver; downOpacity = imageOpacityWhenDown; downOverlay = overlayColourWhenDown; alphaThreshold = (uint8) jlimit (0, 0xff, roundToInt (255.0f * hitTestAlphaThreshold)); repaint(); } Image ImageButton::getCurrentImage() const { if (isDown() || getToggleState()) return getDownImage(); if (isOver()) return getOverImage(); return getNormalImage(); } Image ImageButton::getNormalImage() const { return normalImage; } Image ImageButton::getOverImage() const { return overImage.isValid() ? overImage : normalImage; } Image ImageButton::getDownImage() const { return downImage.isValid() ? downImage : getOverImage(); } void ImageButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { if (! isEnabled()) { isMouseOverButton = false; isButtonDown = false; } Image im (getCurrentImage()); if (im.isValid()) { const int iw = im.getWidth(); const int ih = im.getHeight(); int w = getWidth(); int h = getHeight(); int x = (w - iw) / 2; int y = (h - ih) / 2; if (scaleImageToFit) { if (preserveProportions) { int newW, newH; const float imRatio = ih / (float) iw; const float destRatio = h / (float) w; if (imRatio > destRatio) { newW = roundToInt (h / imRatio); newH = h; } else { newW = w; newH = roundToInt (w * imRatio); } x = (w - newW) / 2; y = (h - newH) / 2; w = newW; h = newH; } else { x = 0; y = 0; } } if (! scaleImageToFit) { w = iw; h = ih; } imageBounds.setBounds (x, y, w, h); const bool useDownImage = isButtonDown || getToggleState(); getLookAndFeel().drawImageButton (g, &im, x, y, w, h, useDownImage ? downOverlay : (isMouseOverButton ? overOverlay : normalOverlay), useDownImage ? downOpacity : (isMouseOverButton ? overOpacity : normalOpacity), *this); } } bool ImageButton::hitTest (int x, int y) { if (alphaThreshold == 0) return true; Image im (getCurrentImage()); return im.isNull() || ((! imageBounds.isEmpty()) && alphaThreshold < im.getPixelAt (((x - imageBounds.getX()) * im.getWidth()) / imageBounds.getWidth(), ((y - imageBounds.getY()) * im.getHeight()) / imageBounds.getHeight()).getAlpha()); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.h000066400000000000000000000203551320201440200320220ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_IMAGEBUTTON_H_INCLUDED #define JUCE_IMAGEBUTTON_H_INCLUDED //============================================================================== /** As the title suggests, this is a button containing an image. The colour and transparency of the image can be set to vary when the button state changes. @see Button, ShapeButton, TextButton */ class JUCE_API ImageButton : public Button { public: //============================================================================== /** Creates an ImageButton. Use setImage() to specify the image to use. The colours and opacities that are specified here can be changed later using setImages(). @param name the name to give the component */ explicit ImageButton (const String& name = String::empty); /** Destructor. */ ~ImageButton(); //============================================================================== /** Sets up the images to draw in various states. @param resizeButtonNowToFitThisImage if true, the button will be immediately resized to the same dimensions as the normal image @param rescaleImagesWhenButtonSizeChanges if true, the image will be rescaled to fit the button when the button's size changes @param preserveImageProportions if true then any rescaling of the image to fit the button will keep the image's x and y proportions correct - i.e. it won't distort its shape, although this might create gaps around the edges @param normalImage the image to use when the button is in its normal state. button no longer needs it. @param imageOpacityWhenNormal the opacity to use when drawing the normal image. @param overlayColourWhenNormal an overlay colour to use to fill the alpha channel of the normal image - if this colour is transparent, no overlay will be drawn. The overlay will be drawn over the top of the image, so you can basically add a solid or semi-transparent colour to the image to brighten or darken it @param overImage the image to use when the mouse is over the button. If you want to use the same image as was set in the normalImage parameter, this value can be a null image. @param imageOpacityWhenOver the opacity to use when drawing the image when the mouse is over the button @param overlayColourWhenOver an overlay colour to use to fill the alpha channel of the image when the mouse is over - if this colour is transparent, no overlay will be drawn @param downImage an image to use when the button is pressed down. If set to a null image, the 'over' image will be drawn instead (or the normal image if there isn't an 'over' image either). @param imageOpacityWhenDown the opacity to use when drawing the image when the button is pressed @param overlayColourWhenDown an overlay colour to use to fill the alpha channel of the image when the button is pressed down - if this colour is transparent, no overlay will be drawn @param hitTestAlphaThreshold if set to zero, the mouse is considered to be over the button whenever it's inside the button's bounding rectangle. If set to values higher than 0, the mouse will only be considered to be over the image when the value of the image's alpha channel at that position is greater than this level. */ void setImages (bool resizeButtonNowToFitThisImage, bool rescaleImagesWhenButtonSizeChanges, bool preserveImageProportions, const Image& normalImage, float imageOpacityWhenNormal, Colour overlayColourWhenNormal, const Image& overImage, float imageOpacityWhenOver, Colour overlayColourWhenOver, const Image& downImage, float imageOpacityWhenDown, Colour overlayColourWhenDown, float hitTestAlphaThreshold = 0.0f); /** Returns the currently set 'normal' image. */ Image getNormalImage() const; /** Returns the image that's drawn when the mouse is over the button. If a valid 'over' image has been set, this will return it; otherwise it'll just return the normal image. */ Image getOverImage() const; /** Returns the image that's drawn when the button is held down. If a valid 'down' image has been set, this will return it; otherwise it'll return the 'over' image or normal image, depending on what's available. */ Image getDownImage() const; //============================================================================== /** This abstract base class is implemented by LookAndFeel classes. */ struct JUCE_API LookAndFeelMethods { virtual ~LookAndFeelMethods() {} virtual void drawImageButton (Graphics&, Image*, int imageX, int imageY, int imageW, int imageH, const Colour& overlayColour, float imageOpacity, ImageButton&) = 0; }; protected: //============================================================================== /** @internal */ bool hitTest (int x, int y) override; /** @internal */ void paintButton (Graphics&, bool isMouseOver, bool isButtonDown) override; private: //============================================================================== bool scaleImageToFit, preserveProportions; uint8 alphaThreshold; Rectangle imageBounds; Image normalImage, overImage, downImage; float normalOpacity, overOpacity, downOpacity; Colour normalOverlay, overOverlay, downOverlay; Image getCurrentImage() const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageButton) }; #endif // JUCE_IMAGEBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp000066400000000000000000000072721320201440200323760ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ShapeButton::ShapeButton (const String& t, Colour n, Colour o, Colour d) : Button (t), normalColour (n), overColour (o), downColour (d), maintainShapeProportions (false), outlineWidth (0.0f) { } ShapeButton::~ShapeButton() {} void ShapeButton::setColours (Colour newNormalColour, Colour newOverColour, Colour newDownColour) { normalColour = newNormalColour; overColour = newOverColour; downColour = newDownColour; } void ShapeButton::setOutline (Colour newOutlineColour, const float newOutlineWidth) { outlineColour = newOutlineColour; outlineWidth = newOutlineWidth; } void ShapeButton::setBorderSize (BorderSize newBorder) { border = newBorder; } void ShapeButton::setShape (const Path& newShape, const bool resizeNowToFitThisShape, const bool maintainShapeProportions_, const bool hasShadow) { shape = newShape; maintainShapeProportions = maintainShapeProportions_; shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point())); setComponentEffect (hasShadow ? &shadow : nullptr); if (resizeNowToFitThisShape) { Rectangle newBounds (shape.getBounds()); if (hasShadow) newBounds = newBounds.expanded (4.0f); shape.applyTransform (AffineTransform::translation (-newBounds.getX(), -newBounds.getY())); setSize (1 + (int) (newBounds.getWidth() + outlineWidth) + border.getLeftAndRight(), 1 + (int) (newBounds.getHeight() + outlineWidth) + border.getTopAndBottom()); } repaint(); } void ShapeButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { if (! isEnabled()) { isMouseOverButton = false; isButtonDown = false; } Rectangle r (border.subtractedFrom (getLocalBounds()).toFloat().reduced (outlineWidth * 0.5f)); if (getComponentEffect() != nullptr) r = r.reduced (2.0f); if (isButtonDown) { const float sizeReductionWhenPressed = 0.04f; r = r.reduced (sizeReductionWhenPressed * r.getWidth(), sizeReductionWhenPressed * r.getHeight()); } const AffineTransform trans (shape.getTransformToScaleToFit (r, maintainShapeProportions)); g.setColour (isButtonDown ? downColour : isMouseOverButton ? overColour : normalColour); g.fillPath (shape, trans); if (outlineWidth > 0.0f) { g.setColour (outlineColour); g.strokePath (shape, PathStrokeType (outlineWidth), trans); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.h000066400000000000000000000100111320201440200320240ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_SHAPEBUTTON_H_INCLUDED #define JUCE_SHAPEBUTTON_H_INCLUDED //============================================================================== /** A button that contains a filled shape. @see Button, ImageButton, TextButton, ArrowButton */ class JUCE_API ShapeButton : public Button { public: //============================================================================== /** Creates a ShapeButton. @param name a name to give the component - see Component::setName() @param normalColour the colour to fill the shape with when the mouse isn't over @param overColour the colour to use when the mouse is over the shape @param downColour the colour to use when the button is in the pressed-down state */ ShapeButton (const String& name, Colour normalColour, Colour overColour, Colour downColour); /** Destructor. */ ~ShapeButton(); //============================================================================== /** Sets the shape to use. @param newShape the shape to use @param resizeNowToFitThisShape if true, the button will be resized to fit the shape's bounds @param maintainShapeProportions if true, the shape's proportions will be kept fixed when the button is resized @param hasDropShadow if true, the button will be given a drop-shadow effect */ void setShape (const Path& newShape, bool resizeNowToFitThisShape, bool maintainShapeProportions, bool hasDropShadow); /** Set the colours to use for drawing the shape. @param normalColour the colour to fill the shape with when the mouse isn't over @param overColour the colour to use when the mouse is over the shape @param downColour the colour to use when the button is in the pressed-down state */ void setColours (Colour normalColour, Colour overColour, Colour downColour); /** Sets up an outline to draw around the shape. @param outlineColour the colour to use @param outlineStrokeWidth the thickness of line to draw */ void setOutline (Colour outlineColour, float outlineStrokeWidth); /** This lets you specify a border to be left around the edge of the button when drawing the shape. */ void setBorderSize (BorderSize border); /** @internal */ void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; private: //============================================================================== Colour normalColour, overColour, downColour, outlineColour; DropShadowEffect shadow; Path shape; BorderSize border; bool maintainShapeProportions; float outlineWidth; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ShapeButton) }; #endif // JUCE_SHAPEBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.cpp000066400000000000000000000037711320201440200322620ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ TextButton::TextButton() : Button (String()) { } TextButton::TextButton (const String& name) : Button (name) { } TextButton::TextButton (const String& name, const String& toolTip) : Button (name) { setTooltip (toolTip); } TextButton::~TextButton() { } void TextButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { LookAndFeel& lf = getLookAndFeel(); lf.drawButtonBackground (g, *this, findColour (getToggleState() ? buttonOnColourId : buttonColourId), isMouseOverButton, isButtonDown); lf.drawButtonText (g, *this, isMouseOverButton, isButtonDown); } void TextButton::colourChanged() { repaint(); } void TextButton::changeWidthToFitText() { changeWidthToFitText (getHeight()); } void TextButton::changeWidthToFitText (const int newHeight) { setSize (getBestWidthForHeight (newHeight), newHeight); } int TextButton::getBestWidthForHeight (int buttonHeight) { return getLookAndFeel().getTextButtonWidthToFitText (*this, buttonHeight); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h000066400000000000000000000114231320201440200317200ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_TEXTBUTTON_H_INCLUDED #define JUCE_TEXTBUTTON_H_INCLUDED //============================================================================== /** A button that uses the standard lozenge-shaped background with a line of text on it. @see Button, DrawableButton */ class JUCE_API TextButton : public Button { public: //============================================================================== /** Creates a TextButton. */ TextButton(); /** Creates a TextButton. @param buttonName the text to put in the button (the component's name is also initially set to this string, but these can be changed later using the setName() and setButtonText() methods) */ explicit TextButton (const String& buttonName); /** Creates a TextButton. @param buttonName the text to put in the button (the component's name is also initially set to this string, but these can be changed later using the setName() and setButtonText() methods) @param toolTip an optional string to use as a toolip */ TextButton (const String& buttonName, const String& toolTip); /** Destructor. */ ~TextButton(); //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the button. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds { buttonColourId = 0x1000100, /**< The colour used to fill the button shape (when the button is toggled 'off'). The look-and-feel class might re-interpret this to add effects, etc. */ buttonOnColourId = 0x1000101, /**< The colour used to fill the button shape (when the button is toggled 'on'). The look-and-feel class might re-interpret this to add effects, etc. */ textColourOffId = 0x1000102, /**< The colour to use for the button's text when the button's toggle state is "off". */ textColourOnId = 0x1000103 /**< The colour to use for the button's text.when the button's toggle state is "on". */ }; //============================================================================== /** Changes this button's width to fit neatly around its current text, without changing its height. */ void changeWidthToFitText(); /** Resizes the button's width to fit neatly around its current text, and gives it the specified height. */ void changeWidthToFitText (int newHeight); /** Returns the width that the LookAndFeel suggests would be best for this button if it had the given height. */ int getBestWidthForHeight (int buttonHeight); //============================================================================== /** @internal */ void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; /** @internal */ void colourChanged() override; private: #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // Note that this method has been removed - instead, see LookAndFeel::getTextButtonFont() virtual int getFont() { return 0; } #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextButton) }; #endif // JUCE_TEXTBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp000066400000000000000000000030241320201440200325460ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ToggleButton::ToggleButton() : Button (String::empty) { setClickingTogglesState (true); } ToggleButton::ToggleButton (const String& buttonText) : Button (buttonText) { setClickingTogglesState (true); } ToggleButton::~ToggleButton() { } void ToggleButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { getLookAndFeel().drawToggleButton (g, *this, isMouseOverButton, isButtonDown); } void ToggleButton::changeWidthToFitText() { getLookAndFeel().changeToggleButtonWidthToFitText (*this); } void ToggleButton::colourChanged() { repaint(); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h000066400000000000000000000061271320201440200322220ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_TOGGLEBUTTON_H_INCLUDED #define JUCE_TOGGLEBUTTON_H_INCLUDED //============================================================================== /** A button that can be toggled on/off. All buttons can be toggle buttons, but this lets you create one of the standard ones which has a tick-box and a text label next to it. @see Button, DrawableButton, TextButton */ class JUCE_API ToggleButton : public Button { public: //============================================================================== /** Creates a ToggleButton. */ ToggleButton(); /** Creates a ToggleButton. @param buttonText the text to put in the button (the component's name is also initially set to this string, but these can be changed later using the setName() and setButtonText() methods) */ explicit ToggleButton (const String& buttonText); /** Destructor. */ ~ToggleButton(); //============================================================================== /** Resizes the button to fit neatly around its current text. The button's height won't be affected, only its width. */ void changeWidthToFitText(); //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the button. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds { textColourId = 0x1006501 /**< The colour to use for the button's text. */ }; protected: //============================================================================== /** @internal */ void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; /** @internal */ void colourChanged() override; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleButton) }; #endif // JUCE_TOGGLEBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp000066400000000000000000000060301320201440200327270ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ToolbarButton::ToolbarButton (const int iid, const String& buttonText, Drawable* const normalIm, Drawable* const toggledOnIm) : ToolbarItemComponent (iid, buttonText, true), normalImage (normalIm), toggledOnImage (toggledOnIm), currentImage (nullptr) { jassert (normalImage != nullptr); } ToolbarButton::~ToolbarButton() { } //============================================================================== bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, bool /*isToolbarVertical*/, int& preferredSize, int& minSize, int& maxSize) { preferredSize = minSize = maxSize = toolbarDepth; return true; } void ToolbarButton::paintButtonArea (Graphics&, int /*width*/, int /*height*/, bool /*isMouseOver*/, bool /*isMouseDown*/) { } void ToolbarButton::contentAreaChanged (const Rectangle&) { buttonStateChanged(); } void ToolbarButton::setCurrentImage (Drawable* const newImage) { if (newImage != currentImage) { removeChildComponent (currentImage); currentImage = newImage; if (currentImage != nullptr) { enablementChanged(); addAndMakeVisible (currentImage); updateDrawable(); } } } void ToolbarButton::updateDrawable() { if (currentImage != nullptr) { currentImage->setInterceptsMouseClicks (false, false); currentImage->setTransformToFit (getContentArea().toFloat(), RectanglePlacement::centred); currentImage->setAlpha (isEnabled() ? 1.0f : 0.5f); } } void ToolbarButton::resized() { ToolbarItemComponent::resized(); updateDrawable(); } void ToolbarButton::enablementChanged() { ToolbarItemComponent::enablementChanged(); updateDrawable(); } Drawable* ToolbarButton::getImageToUse() const { if (getStyle() == Toolbar::textOnly) return nullptr; if (getToggleState() && toggledOnImage != nullptr) return toggledOnImage; return normalImage; } void ToolbarButton::buttonStateChanged() { setCurrentImage (getImageToUse()); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.h000066400000000000000000000100621320201440200323740ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_TOOLBARBUTTON_H_INCLUDED #define JUCE_TOOLBARBUTTON_H_INCLUDED //============================================================================== /** A type of button designed to go on a toolbar. This simple button can have two Drawable objects specified - one for normal use and another one (optionally) for the button's "on" state if it's a toggle button. @see Toolbar, ToolbarItemFactory, ToolbarItemComponent, Drawable, Button */ class JUCE_API ToolbarButton : public ToolbarItemComponent { public: //============================================================================== /** Creates a ToolbarButton. @param itemId the ID for this toolbar item type. This is passed through to the ToolbarItemComponent constructor @param labelText the text to display on the button (if the toolbar is using a style that shows text labels). This is passed through to the ToolbarItemComponent constructor @param normalImage a drawable object that the button should use as its icon. The object that is passed-in here will be kept by this object and will be deleted when no longer needed or when this button is deleted. @param toggledOnImage a drawable object that the button can use as its icon if the button is in a toggled-on state (see the Button::getToggleState() method). If nullptr is passed-in here, then the normal image will be used instead, regardless of the toggle state. The object that is passed-in here will be owned by this object and will be deleted when no longer needed or when this button is deleted. */ ToolbarButton (int itemId, const String& labelText, Drawable* normalImage, Drawable* toggledOnImage); /** Destructor. */ ~ToolbarButton(); //============================================================================== /** @internal */ bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize, int& minSize, int& maxSize) override; /** @internal */ void paintButtonArea (Graphics&, int width, int height, bool isMouseOver, bool isMouseDown) override; /** @internal */ void contentAreaChanged (const Rectangle&) override; /** @internal */ void buttonStateChanged() override; /** @internal */ void resized() override; /** @internal */ void enablementChanged() override; private: //============================================================================== ScopedPointer normalImage, toggledOnImage; Drawable* currentImage; void updateDrawable(); Drawable* getImageToUse() const; void setCurrentImage (Drawable*); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToolbarButton) }; #endif // JUCE_TOOLBARBUTTON_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/000077500000000000000000000000001320201440200265235ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h000066400000000000000000000065731320201440200336740ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_APPLICATIONCOMMANDID_H_INCLUDED #define JUCE_APPLICATIONCOMMANDID_H_INCLUDED //============================================================================== /** A type used to hold the unique ID for an application command. This is a numeric type, so it can be stored as an integer. @see ApplicationCommandInfo, ApplicationCommandManager, ApplicationCommandTarget, KeyPressMappingSet */ typedef int CommandID; //============================================================================== /** A set of general-purpose application command IDs. Because these commands are likely to be used in most apps, they're defined here to help different apps to use the same numeric values for them. Of course you don't have to use these, but some of them are used internally by Juce - e.g. the quit ID is recognised as a command by the JUCEApplication class. @see ApplicationCommandInfo, ApplicationCommandManager, ApplicationCommandTarget, KeyPressMappingSet */ namespace StandardApplicationCommandIDs { enum { /** This command ID should be used to send a "Quit the App" command. This command is recognised by the JUCEApplication class, so if it is invoked and no other ApplicationCommandTarget handles the event first, the JUCEApplication object will catch it and call JUCEApplicationBase::systemRequestedQuit(). */ quit = 0x1001, /** The command ID that should be used to send a "Delete" command. */ del = 0x1002, /** The command ID that should be used to send a "Cut" command. */ cut = 0x1003, /** The command ID that should be used to send a "Copy to clipboard" command. */ copy = 0x1004, /** The command ID that should be used to send a "Paste from clipboard" command. */ paste = 0x1005, /** The command ID that should be used to send a "Select all" command. */ selectAll = 0x1006, /** The command ID that should be used to send a "Deselect all" command. */ deselectAll = 0x1007, /** The command ID that should be used to send a "undo" command. */ undo = 0x1008, /** The command ID that should be used to send a "redo" command. */ redo = 0x1009 }; } #endif // JUCE_APPLICATIONCOMMANDID_H_INCLUDED juce_ApplicationCommandInfo.cpp000066400000000000000000000036011320201440200345340ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ApplicationCommandInfo::ApplicationCommandInfo (const CommandID cid) noexcept : commandID (cid), flags (0) { } void ApplicationCommandInfo::setInfo (const String& shortName_, const String& description_, const String& categoryName_, const int flags_) noexcept { shortName = shortName_; description = description_; categoryName = categoryName_; flags = flags_; } void ApplicationCommandInfo::setActive (const bool b) noexcept { if (b) flags &= ~isDisabled; else flags |= isDisabled; } void ApplicationCommandInfo::setTicked (const bool b) noexcept { if (b) flags |= isTicked; else flags &= ~isTicked; } void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, ModifierKeys modifiers) noexcept { defaultKeypresses.add (KeyPress (keyCode, modifiers, 0)); } juce_ApplicationCommandInfo.h000066400000000000000000000162221320201440200342040ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_APPLICATIONCOMMANDINFO_H_INCLUDED #define JUCE_APPLICATIONCOMMANDINFO_H_INCLUDED //============================================================================== /** Holds information describing an application command. This object is used to pass information about a particular command, such as its name, description and other usage flags. When an ApplicationCommandTarget is asked to provide information about the commands it can perform, this is the structure gets filled-in to describe each one. @see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(), ApplicationCommandManager */ struct JUCE_API ApplicationCommandInfo { //============================================================================== explicit ApplicationCommandInfo (CommandID commandID) noexcept; //============================================================================== /** Sets a number of the structures values at once. The meanings of each of the parameters is described below, in the appropriate member variable's description. */ void setInfo (const String& shortName, const String& description, const String& categoryName, int flags) noexcept; /** An easy way to set or remove the isDisabled bit in the structure's flags field. If isActive is true, the flags member has the isDisabled bit cleared; if isActive is false, the bit is set. */ void setActive (bool isActive) noexcept; /** An easy way to set or remove the isTicked bit in the structure's flags field. */ void setTicked (bool isTicked) noexcept; /** Handy method for adding a keypress to the defaultKeypresses array. This is just so you can write things like: @code myinfo.addDefaultKeypress ('s', ModifierKeys::commandModifier); @endcode instead of @code myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier)); @endcode */ void addDefaultKeypress (int keyCode, ModifierKeys modifiers) noexcept; //============================================================================== /** The command's unique ID number. */ CommandID commandID; /** A short name to describe the command. This should be suitable for use in menus, on buttons that trigger the command, etc. You can use the setInfo() method to quickly set this and some of the command's other properties. */ String shortName; /** A longer description of the command. This should be suitable for use in contexts such as a KeyMappingEditorComponent or pop-up tooltip describing what the command does. You can use the setInfo() method to quickly set this and some of the command's other properties. */ String description; /** A named category that the command fits into. You can give your commands any category you like, and these will be displayed in contexts such as the KeyMappingEditorComponent, where the category is used to group commands together. You can use the setInfo() method to quickly set this and some of the command's other properties. */ String categoryName; /** A list of zero or more keypresses that should be used as the default keys for this command. Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in this list to initialise the default set of key-to-command mappings. @see addDefaultKeypress */ Array defaultKeypresses; //============================================================================== /** Flags describing the ways in which this command should be used. A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags variable. */ enum CommandFlags { /** Indicates that the command can't currently be performed. The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's not currently permissable to perform the command. If the flag is set, then components that trigger the command, e.g. PopupMenu, may choose to grey-out the command or show themselves as not being enabled. @see ApplicationCommandInfo::setActive */ isDisabled = 1 << 0, /** Indicates that the command should have a tick next to it on a menu. If your command is shown on a menu and this is set, it'll show a tick next to it. Other components such as buttons may also use this flag to indicate that it is a value that can be toggled, and is currently in the 'on' state. @see ApplicationCommandInfo::setTicked */ isTicked = 1 << 1, /** If this flag is present, then when a KeyPressMappingSet invokes the command, it will call the command twice, once on key-down and again on key-up. @see ApplicationCommandTarget::InvocationInfo */ wantsKeyUpDownCallbacks = 1 << 2, /** If this flag is present, then a KeyMappingEditorComponent will not display the command in its list. */ hiddenFromKeyEditor = 1 << 3, /** If this flag is present, then a KeyMappingEditorComponent will display the command in its list, but won't allow the assigned keypress to be changed. */ readOnlyInKeyEditor = 1 << 4, /** If this flag is present and the command is invoked from a keypress, then any buttons or menus that are also connected to the command will not flash to indicate that they've been triggered. */ dontTriggerVisualFeedback = 1 << 5 }; /** A bitwise-OR of the values specified in the CommandFlags enum. You can use the setInfo() method to quickly set this and some of the command's other properties. */ int flags; }; #endif // JUCE_APPLICATIONCOMMANDINFO_H_INCLUDED juce_ApplicationCommandManager.cpp000066400000000000000000000256241320201440200352240ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ApplicationCommandManager::ApplicationCommandManager() : firstTarget (nullptr) { keyMappings = new KeyPressMappingSet (*this); Desktop::getInstance().addFocusChangeListener (this); } ApplicationCommandManager::~ApplicationCommandManager() { Desktop::getInstance().removeFocusChangeListener (this); keyMappings = nullptr; } //============================================================================== void ApplicationCommandManager::clearCommands() { commands.clear(); keyMappings->clearAllKeyPresses(); triggerAsyncUpdate(); } void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand) { // zero isn't a valid command ID! jassert (newCommand.commandID != 0); // the name isn't optional! jassert (newCommand.shortName.isNotEmpty()); if (ApplicationCommandInfo* command = getMutableCommandForID (newCommand.commandID)) { // Trying to re-register the same command ID with different parameters can often indicate a typo. // This assertion is here because I've found it useful catching some mistakes, but it may also cause // false alarms if you're deliberately updating some flags for a command. jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)) == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))); *command = newCommand; } else { ApplicationCommandInfo* const newInfo = new ApplicationCommandInfo (newCommand); newInfo->flags &= ~ApplicationCommandInfo::isTicked; commands.add (newInfo); keyMappings->resetToDefaultMapping (newCommand.commandID); triggerAsyncUpdate(); } } void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target) { if (target != nullptr) { Array commandIDs; target->getAllCommands (commandIDs); for (int i = 0; i < commandIDs.size(); ++i) { ApplicationCommandInfo info (commandIDs.getUnchecked(i)); target->getCommandInfo (info.commandID, info); registerCommand (info); } } } void ApplicationCommandManager::removeCommand (const CommandID commandID) { for (int i = commands.size(); --i >= 0;) { if (commands.getUnchecked (i)->commandID == commandID) { commands.remove (i); triggerAsyncUpdate(); const Array keys (keyMappings->getKeyPressesAssignedToCommand (commandID)); for (int j = keys.size(); --j >= 0;) keyMappings->removeKeyPress (keys.getReference (j)); } } } void ApplicationCommandManager::commandStatusChanged() { triggerAsyncUpdate(); } //============================================================================== ApplicationCommandInfo* ApplicationCommandManager::getMutableCommandForID (CommandID commandID) const noexcept { for (int i = commands.size(); --i >= 0;) if (commands.getUnchecked(i)->commandID == commandID) return commands.getUnchecked(i); return nullptr; } const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const CommandID commandID) const noexcept { return getMutableCommandForID (commandID); } String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) const noexcept { if (const ApplicationCommandInfo* const ci = getCommandForID (commandID)) return ci->shortName; return String(); } String ApplicationCommandManager::getDescriptionOfCommand (const CommandID commandID) const noexcept { if (const ApplicationCommandInfo* const ci = getCommandForID (commandID)) return ci->description.isNotEmpty() ? ci->description : ci->shortName; return String(); } StringArray ApplicationCommandManager::getCommandCategories() const { StringArray s; for (int i = 0; i < commands.size(); ++i) s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false); return s; } Array ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const { Array results; for (int i = 0; i < commands.size(); ++i) if (commands.getUnchecked(i)->categoryName == categoryName) results.add (commands.getUnchecked(i)->commandID); return results; } //============================================================================== bool ApplicationCommandManager::invokeDirectly (const CommandID commandID, const bool asynchronously) { ApplicationCommandTarget::InvocationInfo info (commandID); info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct; return invoke (info, asynchronously); } bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& inf, const bool asynchronously) { // This call isn't thread-safe for use from a non-UI thread without locking the message // manager first.. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); bool ok = false; ApplicationCommandInfo commandInfo (0); if (ApplicationCommandTarget* const target = getTargetForCommand (inf.commandID, commandInfo)) { ApplicationCommandTarget::InvocationInfo info (inf); info.commandFlags = commandInfo.flags; sendListenerInvokeCallback (info); ok = target->invoke (info, asynchronously); commandStatusChanged(); } return ok; } //============================================================================== ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (const CommandID) { return firstTarget != nullptr ? firstTarget : findDefaultComponentTarget(); } void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* const newTarget) noexcept { firstTarget = newTarget; } ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (const CommandID commandID, ApplicationCommandInfo& upToDateInfo) { ApplicationCommandTarget* target = getFirstCommandTarget (commandID); if (target == nullptr) target = JUCEApplication::getInstance(); if (target != nullptr) target = target->getTargetForCommand (commandID); if (target != nullptr) { upToDateInfo.commandID = commandID; target->getCommandInfo (commandID, upToDateInfo); } return target; } //============================================================================== ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c) { ApplicationCommandTarget* target = dynamic_cast (c); if (target == nullptr && c != nullptr) target = c->findParentComponentOfClass(); return target; } ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget() { Component* c = Component::getCurrentlyFocusedComponent(); if (c == nullptr) { if (TopLevelWindow* const activeWindow = TopLevelWindow::getActiveTopLevelWindow()) { c = activeWindow->getPeer()->getLastFocusedSubcomponent(); if (c == nullptr) c = activeWindow; } } if (c == nullptr && Process::isForegroundProcess()) { Desktop& desktop = Desktop::getInstance(); // getting a bit desperate now: try all desktop comps.. for (int i = desktop.getNumComponents(); --i >= 0;) if (ComponentPeer* const peer = desktop.getComponent(i)->getPeer()) if (ApplicationCommandTarget* const target = findTargetForComponent (peer->getLastFocusedSubcomponent())) return target; } if (c != nullptr) { // if we're focused on a ResizableWindow, chances are that it's the content // component that really should get the event. And if not, the event will // still be passed up to the top level window anyway, so let's send it to the // content comp. if (ResizableWindow* const resizableWindow = dynamic_cast (c)) if (Component* const content = resizableWindow->getContentComponent()) c = content; if (ApplicationCommandTarget* const target = findTargetForComponent (c)) return target; } return JUCEApplication::getInstance(); } //============================================================================== void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* const listener) { listeners.add (listener); } void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* const listener) { listeners.remove (listener); } void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info) { listeners.call (&ApplicationCommandManagerListener::applicationCommandInvoked, info); } void ApplicationCommandManager::handleAsyncUpdate() { listeners.call (&ApplicationCommandManagerListener::applicationCommandListChanged); } void ApplicationCommandManager::globalFocusChanged (Component*) { commandStatusChanged(); } juce_ApplicationCommandManager.h000066400000000000000000000402541320201440200346650ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_APPLICATIONCOMMANDMANAGER_H_INCLUDED #define JUCE_APPLICATIONCOMMANDMANAGER_H_INCLUDED //============================================================================== /** One of these objects holds a list of all the commands your app can perform, and despatches these commands when needed. Application commands are a good way to trigger actions in your app, e.g. "Quit", "Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands to invoke automatically, which means you don't have to handle the result of a menu or button click manually. Commands are despatched to ApplicationCommandTarget objects which can choose which events they want to handle. This architecture also allows for nested ApplicationCommandTargets, so that for example you could have two different objects, one inside the other, both of which can respond to a "delete" command. Depending on which one has focus, the command will be sent to the appropriate place, regardless of whether it was triggered by a menu, keypress or some other method. To set up your app to use commands, you'll need to do the following: - Create a global ApplicationCommandManager to hold the list of all possible commands. (This will also manage a set of key-mappings for them). - Make some of your UI components (or other objects) inherit from ApplicationCommandTarget. This allows the object to provide a list of commands that it can perform, and to handle them. - Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(), or ApplicationCommandManager::registerCommand(). - If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings() method to access the key-mapper object, which you will need to register as a key-listener in whatever top-level component you're using. See the KeyPressMappingSet class for more help about setting this up. - Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to cause these commands to be invoked automatically. - Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly(). When a command is invoked, the ApplicationCommandManager will try to choose the best ApplicationCommandTarget to receive the specified command. To do this it will use the current keyboard focus to see which component might be interested, and will search the component hierarchy for those that also implement the ApplicationCommandTarget interface. If an ApplicationCommandTarget isn't interested in the command that is being invoked, then the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget() method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns nullptr. At this point if the command still hasn't been performed, it will be passed to the current JUCEApplication object (which is itself an ApplicationCommandTarget). To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command, you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose the object yourself. @see ApplicationCommandTarget, ApplicationCommandInfo */ class JUCE_API ApplicationCommandManager : private AsyncUpdater, private FocusChangeListener { public: //============================================================================== /** Creates an ApplicationCommandManager. Once created, you'll need to register all your app's commands with it, using ApplicationCommandManager::registerAllCommandsForTarget() or ApplicationCommandManager::registerCommand(). */ ApplicationCommandManager(); /** Destructor. Make sure that you don't delete this if pointers to it are still being used by objects such as PopupMenus or Buttons. */ virtual ~ApplicationCommandManager(); //============================================================================== /** Clears the current list of all commands. Note that this will also clear the contents of the KeyPressMappingSet. */ void clearCommands(); /** Adds a command to the list of registered commands. @see registerAllCommandsForTarget */ void registerCommand (const ApplicationCommandInfo& newCommand); /** Adds all the commands that this target publishes to the manager's list. This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo() to get details about all the commands that this target can do, and will call registerCommand() to add each one to the manger's list. @see registerCommand */ void registerAllCommandsForTarget (ApplicationCommandTarget* target); /** Removes the command with a specified ID. Note that this will also remove any key mappings that are mapped to the command. */ void removeCommand (CommandID commandID); /** This should be called to tell the manager that one of its registered commands may have changed its active status. Because the command manager only finds out whether a command is active or inactive by querying the current ApplicationCommandTarget, this is used to tell it that things may have changed. It allows things like buttons to update their enablement, etc. This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged() for any registered listeners. */ void commandStatusChanged(); //============================================================================== /** Returns the number of commands that have been registered. @see registerCommand */ int getNumCommands() const noexcept { return commands.size(); } /** Returns the details about one of the registered commands. The index is between 0 and (getNumCommands() - 1). */ const ApplicationCommandInfo* getCommandForIndex (int index) const noexcept { return commands [index]; } /** Returns the details about a given command ID. This will search the list of registered commands for one with the given command ID number, and return its associated info. If no matching command is found, this will return nullptr. */ const ApplicationCommandInfo* getCommandForID (CommandID commandID) const noexcept; /** Returns the name field for a command. An empty string is returned if no command with this ID has been registered. @see getDescriptionOfCommand */ String getNameOfCommand (CommandID commandID) const noexcept; /** Returns the description field for a command. An empty string is returned if no command with this ID has been registered. If the command has no description, this will return its short name field instead. @see getNameOfCommand */ String getDescriptionOfCommand (CommandID commandID) const noexcept; /** Returns the list of categories. This will go through all registered commands, and return a list of all the distinct categoryName values from their ApplicationCommandInfo structure. @see getCommandsInCategory() */ StringArray getCommandCategories() const; /** Returns a list of all the command UIDs in a particular category. @see getCommandCategories() */ Array getCommandsInCategory (const String& categoryName) const; //============================================================================== /** Returns the manager's internal set of key mappings. This object can be used to edit the keypresses. To actually link this object up to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet class. @see KeyPressMappingSet */ KeyPressMappingSet* getKeyMappings() const noexcept { return keyMappings; } //============================================================================== /** Invokes the given command directly, sending it to the default target. This is just an easy way to call invoke() without having to fill out the InvocationInfo structure. */ bool invokeDirectly (CommandID commandID, bool asynchronously); /** Sends a command to the default target. This will choose a target using getFirstCommandTarget(), and send the specified command to it using the ApplicationCommandTarget::invoke() method. This means that if the first target can't handle the command, it will be passed on to targets further down the chain (see ApplicationCommandTarget::invoke() for more info). @param invocationInfo this must be correctly filled-in, describing the context for the invocation. @param asynchronously if false, the command will be performed before this method returns. If true, a message will be posted so that the command will be performed later on the message thread, and this method will return immediately. @see ApplicationCommandTarget::invoke */ bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo, bool asynchronously); //============================================================================== /** Chooses the ApplicationCommandTarget to which a command should be sent. Whenever the manager needs to know which target a command should be sent to, it calls this method to determine the first one to try. By default, this method will return the target that was set by calling setFirstCommandTarget(). If no target is set, it will return the result of findDefaultComponentTarget(). If you need to make sure all commands go via your own custom target, then you can either use setFirstCommandTarget() to specify a single target, or override this method if you need more complex logic to choose one. It may return nullptr if no targets are available. @see getTargetForCommand, invoke, invokeDirectly */ virtual ApplicationCommandTarget* getFirstCommandTarget (CommandID commandID); /** Sets a target to be returned by getFirstCommandTarget(). If this is set to nullptr, then getFirstCommandTarget() will by default return the result of findDefaultComponentTarget(). If you use this to set a target, make sure you call setFirstCommandTarget(nullptr) before deleting the target object. */ void setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept; /** Tries to find the best target to use to perform a given command. This will call getFirstCommandTarget() to find the preferred target, and will check whether that target can handle the given command. If it can't, then it'll use ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and so on until no more are available. If no targets are found that can perform the command, this method will return nullptr. If a target is found, then it will get the target to fill-in the upToDateInfo structure with the latest info about that command, so that the caller can see whether the command is disabled, ticked, etc. */ ApplicationCommandTarget* getTargetForCommand (CommandID commandID, ApplicationCommandInfo& upToDateInfo); //============================================================================== /** Registers a listener that will be called when various events occur. */ void addListener (ApplicationCommandManagerListener* listener); /** Deregisters a previously-added listener. */ void removeListener (ApplicationCommandManagerListener* listener); //============================================================================== /** Looks for a suitable command target based on which Components have the keyboard focus. This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(), but is exposed here in case it's useful. It tries to pick the best ApplicationCommandTarget by looking at focused components, top level windows, etc., and using the findTargetForComponent() method. */ static ApplicationCommandTarget* findDefaultComponentTarget(); /** Examines this component and all its parents in turn, looking for the first one which is an ApplicationCommandTarget. Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them implement that class. */ static ApplicationCommandTarget* findTargetForComponent (Component*); private: //============================================================================== OwnedArray commands; ListenerList listeners; ScopedPointer keyMappings; ApplicationCommandTarget* firstTarget; void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo&); void handleAsyncUpdate() override; void globalFocusChanged (Component*) override; ApplicationCommandInfo* getMutableCommandForID (CommandID) const noexcept; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // This is just here to cause a compile error in old code that hasn't been changed to use the new // version of this method. virtual short getFirstCommandTarget() { return 0; } #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager) }; //============================================================================== /** A listener that receives callbacks from an ApplicationCommandManager when commands are invoked or the command list is changed. @see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener */ class JUCE_API ApplicationCommandManagerListener { public: //============================================================================== /** Destructor. */ virtual ~ApplicationCommandManagerListener() {} /** Called when an app command is about to be invoked. */ virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) = 0; /** Called when commands are registered or deregistered from the command manager, or when commands are made active or inactive. Note that if you're using this to watch for changes to whether a command is disabled, you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called whenever the status of your command might have changed. */ virtual void applicationCommandListChanged() = 0; }; #endif // JUCE_APPLICATIONCOMMANDMANAGER_H_INCLUDED juce_ApplicationCommandTarget.cpp000066400000000000000000000126001320201440200350660ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class ApplicationCommandTarget::CommandMessage : public MessageManager::MessageBase { public: CommandMessage (ApplicationCommandTarget* const target, const InvocationInfo& inf) : owner (target), info (inf) { } void messageCallback() override { if (ApplicationCommandTarget* const target = owner) target->tryToInvoke (info, false); } private: WeakReference owner; const InvocationInfo info; JUCE_DECLARE_NON_COPYABLE (CommandMessage) }; //============================================================================== ApplicationCommandTarget::ApplicationCommandTarget() { } ApplicationCommandTarget::~ApplicationCommandTarget() { masterReference.clear(); } //============================================================================== bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async) { if (isCommandActive (info.commandID)) { if (async) { (new CommandMessage (this, info))->post(); return true; } if (perform (info)) return true; // Hmm.. your target claimed that it could perform this command, but failed to do so. // If it can't do it at the moment for some reason, it should clear the 'isActive' flag // when it returns the command's info. jassertfalse; } return false; } ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent() { if (Component* const c = dynamic_cast (this)) return c->findParentComponentOfClass(); return nullptr; } ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID) { ApplicationCommandTarget* target = this; int depth = 0; while (target != nullptr) { Array commandIDs; target->getAllCommands (commandIDs); if (commandIDs.contains (commandID)) return target; target = target->getNextCommandTarget(); ++depth; jassert (depth < 100); // could be a recursive command chain?? jassert (target != this); // definitely a recursive command chain! if (depth > 100 || target == this) break; } if (target == nullptr) { target = JUCEApplication::getInstance(); if (target != nullptr) { Array commandIDs; target->getAllCommands (commandIDs); if (commandIDs.contains (commandID)) return target; } } return nullptr; } bool ApplicationCommandTarget::isCommandActive (const CommandID commandID) { ApplicationCommandInfo info (commandID); info.flags = ApplicationCommandInfo::isDisabled; getCommandInfo (commandID, info); return (info.flags & ApplicationCommandInfo::isDisabled) == 0; } //============================================================================== bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async) { ApplicationCommandTarget* target = this; int depth = 0; while (target != nullptr) { if (target->tryToInvoke (info, async)) return true; target = target->getNextCommandTarget(); ++depth; jassert (depth < 100); // could be a recursive command chain?? jassert (target != this); // definitely a recursive command chain! if (depth > 100 || target == this) break; } if (target == nullptr) { target = JUCEApplication::getInstance(); if (target != nullptr) return target->tryToInvoke (info, async); } return false; } bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously) { ApplicationCommandTarget::InvocationInfo info (commandID); info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct; return invoke (info, asynchronously); } //============================================================================== ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID command) : commandID (command), commandFlags (0), invocationMethod (direct), originatingComponent (nullptr), isKeyDown (false), millisecsSinceKeyPressed (0) { } juce_ApplicationCommandTarget.h000066400000000000000000000243201320201440200345350ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_APPLICATIONCOMMANDTARGET_H_INCLUDED #define JUCE_APPLICATIONCOMMANDTARGET_H_INCLUDED //============================================================================== /** A command target publishes a list of command IDs that it can perform. An ApplicationCommandManager despatches commands to targets, which must be able to provide information about what commands they can handle. To create a target, you'll need to inherit from this class, implementing all of its pure virtual methods. For info about how a target is chosen to receive a command, see ApplicationCommandManager::getFirstCommandTarget(). @see ApplicationCommandManager, ApplicationCommandInfo */ class JUCE_API ApplicationCommandTarget { public: //============================================================================== /** Creates a command target. */ ApplicationCommandTarget(); /** Destructor. */ virtual ~ApplicationCommandTarget(); //============================================================================== /** Contains contextual details about the invocation of a command. */ struct JUCE_API InvocationInfo { //============================================================================== InvocationInfo (const CommandID commandID); //============================================================================== /** The UID of the command that should be performed. */ CommandID commandID; /** The command's flags. See ApplicationCommandInfo for a description of these flag values. */ int commandFlags; //============================================================================== /** The types of context in which the command might be called. */ enum InvocationMethod { direct = 0, /**< The command is being invoked directly by a piece of code. */ fromKeyPress, /**< The command is being invoked by a key-press. */ fromMenu, /**< The command is being invoked by a menu selection. */ fromButton /**< The command is being invoked by a button click. */ }; /** The type of event that triggered this command. */ InvocationMethod invocationMethod; //============================================================================== /** If triggered by a keypress or menu, this will be the component that had the keyboard focus at the time. If triggered by a button, it may be set to that component, or it may be null. */ Component* originatingComponent; //============================================================================== /** The keypress that was used to invoke it. Note that this will be an invalid keypress if the command was invoked by some other means than a keyboard shortcut. */ KeyPress keyPress; /** True if the callback is being invoked when the key is pressed, false if the key is being released. @see KeyPressMappingSet::addCommand() */ bool isKeyDown; /** If the key is being released, this indicates how long it had been held down for. (Only relevant if isKeyDown is false.) */ int millisecsSinceKeyPressed; }; //============================================================================== /** This must return the next target to try after this one. When a command is being sent, and the first target can't handle that command, this method is used to determine the next target that should be tried. It may return nullptr if it doesn't know of another target. If your target is a Component, you would usually use the findFirstTargetParentComponent() method to return a parent component that might want to handle it. @see invoke */ virtual ApplicationCommandTarget* getNextCommandTarget() = 0; /** This must return a complete list of commands that this target can handle. Your target should add all the command IDs that it handles to the array that is passed-in. */ virtual void getAllCommands (Array& commands) = 0; /** This must provide details about one of the commands that this target can perform. This will be called with one of the command IDs that the target provided in its getAllCommands() methods. It should fill-in all appropriate fields of the ApplicationCommandInfo structure with suitable information about the command. (The commandID field will already have been filled-in by the caller). The easiest way to set the info is using the ApplicationCommandInfo::setInfo() method to set all the fields at once. If the command is currently inactive for some reason, this method must use ApplicationCommandInfo::setActive() to make that clear, (or it should set the isDisabled bit of the ApplicationCommandInfo::flags field). Any default key-presses for the command should be appended to the ApplicationCommandInfo::defaultKeypresses field. Note that if you change something that affects the status of the commands that would be returned by this method (e.g. something that makes some commands active or inactive), you should call ApplicationCommandManager::commandStatusChanged() to cause the manager to refresh its status. */ virtual void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) = 0; /** This must actually perform the specified command. If this target is able to perform the command specified by the commandID field of the InvocationInfo structure, then it should do so, and must return true. If it can't handle this command, it should return false, which tells the caller to pass the command on to the next target in line. @see invoke, ApplicationCommandManager::invoke */ virtual bool perform (const InvocationInfo& info) = 0; //============================================================================== /** Makes this target invoke a command. Your code can call this method to invoke a command on this target, but normally you'd call it indirectly via ApplicationCommandManager::invoke() or ApplicationCommandManager::invokeDirectly(). If this target can perform the given command, it will call its perform() method to do so. If not, then getNextCommandTarget() will be used to determine the next target to try, and the command will be passed along to it. @param invocationInfo this must be correctly filled-in, describing the context for the invocation. @param asynchronously if false, the command will be performed before this method returns. If true, a message will be posted so that the command will be performed later on the message thread, and this method will return immediately. @see perform, ApplicationCommandManager::invoke */ bool invoke (const InvocationInfo& invocationInfo, const bool asynchronously); /** Invokes a given command directly on this target. This is just an easy way to call invoke() without having to fill out the InvocationInfo structure. */ bool invokeDirectly (const CommandID commandID, const bool asynchronously); //============================================================================== /** Searches this target and all subsequent ones for the first one that can handle the specified command. This will use getNextCommandTarget() to determine the chain of targets to try after this one. */ ApplicationCommandTarget* getTargetForCommand (const CommandID commandID); /** Checks whether this command can currently be performed by this target. This will return true only if a call to getCommandInfo() doesn't set the isDisabled flag to indicate that the command is inactive. */ bool isCommandActive (const CommandID commandID); /** If this object is a Component, this method will seach upwards in its current UI hierarchy for the next parent component that implements the ApplicationCommandTarget class. If your target is a Component, this is a very handy method to use in your getNextCommandTarget() implementation. */ ApplicationCommandTarget* findFirstTargetParentComponent(); private: //============================================================================== WeakReference::Master masterReference; friend class WeakReference; class CommandMessage; friend class CommandMessage; bool tryToInvoke (const InvocationInfo&, bool async); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget) }; #endif // JUCE_APPLICATIONCOMMANDTARGET_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp000066400000000000000000000336361320201440200340050ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager& cm) : commandManager (cm) { Desktop::getInstance().addFocusChangeListener (this); } KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other) : KeyListener(), ChangeBroadcaster(), FocusChangeListener(), commandManager (other.commandManager) { Desktop::getInstance().addFocusChangeListener (this); } KeyPressMappingSet::~KeyPressMappingSet() { Desktop::getInstance().removeFocusChangeListener (this); } //============================================================================== Array KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const { for (int i = 0; i < mappings.size(); ++i) if (mappings.getUnchecked(i)->commandID == commandID) return mappings.getUnchecked (i)->keypresses; return Array(); } void KeyPressMappingSet::addKeyPress (const CommandID commandID, const KeyPress& newKeyPress, int insertIndex) { // If you specify an upper-case letter but no shift key, how is the user supposed to press it!? // Stick to lower-case letters when defining a keypress, to avoid ambiguity. jassert (! (CharacterFunctions::isUpperCase (newKeyPress.getTextCharacter()) && ! newKeyPress.getModifiers().isShiftDown())); if (findCommandForKeyPress (newKeyPress) != commandID) { if (newKeyPress.isValid()) { for (int i = mappings.size(); --i >= 0;) { if (mappings.getUnchecked(i)->commandID == commandID) { mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress); sendChangeMessage(); return; } } if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID)) { CommandMapping* const cm = new CommandMapping(); cm->commandID = commandID; cm->keypresses.add (newKeyPress); cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0; mappings.add (cm); sendChangeMessage(); } else { // If you hit this, you're trying to attach a keypress to a command ID that // doesn't exist, so the key is not being attached. jassertfalse; } } } } static void addKeyPresses (KeyPressMappingSet& set, const ApplicationCommandInfo* const ci) { for (int j = 0; j < ci->defaultKeypresses.size(); ++j) set.addKeyPress (ci->commandID, ci->defaultKeypresses.getReference (j)); } void KeyPressMappingSet::resetToDefaultMappings() { mappings.clear(); for (int i = 0; i < commandManager.getNumCommands(); ++i) addKeyPresses (*this, commandManager.getCommandForIndex (i)); sendChangeMessage(); } void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID) { clearAllKeyPresses (commandID); if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID)) addKeyPresses (*this, ci); } void KeyPressMappingSet::clearAllKeyPresses() { if (mappings.size() > 0) { sendChangeMessage(); mappings.clear(); } } void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID) { for (int i = mappings.size(); --i >= 0;) { if (mappings.getUnchecked(i)->commandID == commandID) { mappings.remove (i); sendChangeMessage(); } } } void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress) { if (keypress.isValid()) { for (int i = mappings.size(); --i >= 0;) { CommandMapping& cm = *mappings.getUnchecked(i); for (int j = cm.keypresses.size(); --j >= 0;) { if (keypress == cm.keypresses [j]) { cm.keypresses.remove (j); sendChangeMessage(); } } } } } void KeyPressMappingSet::removeKeyPress (const CommandID commandID, const int keyPressIndex) { for (int i = mappings.size(); --i >= 0;) { if (mappings.getUnchecked(i)->commandID == commandID) { mappings.getUnchecked(i)->keypresses.remove (keyPressIndex); sendChangeMessage(); break; } } } //============================================================================== CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const noexcept { for (int i = 0; i < mappings.size(); ++i) if (mappings.getUnchecked(i)->keypresses.contains (keyPress)) return mappings.getUnchecked(i)->commandID; return 0; } bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept { for (int i = mappings.size(); --i >= 0;) if (mappings.getUnchecked(i)->commandID == commandID) return mappings.getUnchecked(i)->keypresses.contains (keyPress); return false; } void KeyPressMappingSet::invokeCommand (const CommandID commandID, const KeyPress& key, const bool isKeyDown, const int millisecsSinceKeyPressed, Component* const originatingComponent) const { ApplicationCommandTarget::InvocationInfo info (commandID); info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress; info.isKeyDown = isKeyDown; info.keyPress = key; info.millisecsSinceKeyPressed = millisecsSinceKeyPressed; info.originatingComponent = originatingComponent; commandManager.invoke (info, false); } //============================================================================== bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion) { if (xmlVersion.hasTagName ("KEYMAPPINGS")) { if (xmlVersion.getBoolAttribute ("basedOnDefaults", true)) { // if the XML was created as a set of differences from the default mappings, // (i.e. by calling createXml (true)), then we need to first restore the defaults. resetToDefaultMappings(); } else { // if the XML was created calling createXml (false), then we need to clear all // the keys and treat the xml as describing the entire set of mappings. clearAllKeyPresses(); } forEachXmlChildElement (xmlVersion, map) { const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32(); if (commandId != 0) { const KeyPress key (KeyPress::createFromDescription (map->getStringAttribute ("key"))); if (map->hasTagName ("MAPPING")) { addKeyPress (commandId, key); } else if (map->hasTagName ("UNMAPPING")) { for (int i = mappings.size(); --i >= 0;) if (mappings.getUnchecked(i)->commandID == commandId) mappings.getUnchecked(i)->keypresses.removeAllInstancesOf (key); } } } return true; } return false; } XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const { ScopedPointer defaultSet; if (saveDifferencesFromDefaultSet) { defaultSet = new KeyPressMappingSet (commandManager); defaultSet->resetToDefaultMappings(); } XmlElement* const doc = new XmlElement ("KEYMAPPINGS"); doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet); for (int i = 0; i < mappings.size(); ++i) { const CommandMapping& cm = *mappings.getUnchecked(i); for (int j = 0; j < cm.keypresses.size(); ++j) { if (defaultSet == nullptr || ! defaultSet->containsMapping (cm.commandID, cm.keypresses.getReference (j))) { XmlElement* const map = doc->createNewChildElement ("MAPPING"); map->setAttribute ("commandId", String::toHexString ((int) cm.commandID)); map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID)); map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription()); } } } if (defaultSet != nullptr) { for (int i = 0; i < defaultSet->mappings.size(); ++i) { const CommandMapping& cm = *defaultSet->mappings.getUnchecked(i); for (int j = 0; j < cm.keypresses.size(); ++j) { if (! containsMapping (cm.commandID, cm.keypresses.getReference (j))) { XmlElement* const map = doc->createNewChildElement ("UNMAPPING"); map->setAttribute ("commandId", String::toHexString ((int) cm.commandID)); map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID)); map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription()); } } } } return doc; } //============================================================================== bool KeyPressMappingSet::keyPressed (const KeyPress& key, Component* const originatingComponent) { bool commandWasDisabled = false; for (int i = 0; i < mappings.size(); ++i) { CommandMapping& cm = *mappings.getUnchecked(i); if (cm.keypresses.contains (key)) { if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (cm.commandID)) { if ((ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0) { ApplicationCommandInfo info (0); if (commandManager.getTargetForCommand (cm.commandID, info) != nullptr) { if ((info.flags & ApplicationCommandInfo::isDisabled) == 0) { invokeCommand (cm.commandID, key, true, 0, originatingComponent); return true; } commandWasDisabled = true; } } } } } if (originatingComponent != nullptr && commandWasDisabled) originatingComponent->getLookAndFeel().playAlertSound(); return false; } bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent) { bool used = false; const uint32 now = Time::getMillisecondCounter(); for (int i = mappings.size(); --i >= 0;) { CommandMapping& cm = *mappings.getUnchecked(i); if (cm.wantsKeyUpDownCallbacks) { for (int j = cm.keypresses.size(); --j >= 0;) { const KeyPress key (cm.keypresses.getReference (j)); const bool isDown = key.isCurrentlyDown(); int keyPressEntryIndex = 0; bool wasDown = false; for (int k = keysDown.size(); --k >= 0;) { if (key == keysDown.getUnchecked(k)->key) { keyPressEntryIndex = k; wasDown = true; used = true; break; } } if (isDown != wasDown) { int millisecs = 0; if (isDown) { KeyPressTime* const k = new KeyPressTime(); k->key = key; k->timeWhenPressed = now; keysDown.add (k); } else { const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed; if (now > pressTime) millisecs = (int) (now - pressTime); keysDown.remove (keyPressEntryIndex); } invokeCommand (cm.commandID, key, isDown, millisecs, originatingComponent); used = true; } } } } return used; } void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent) { if (focusedComponent != nullptr) focusedComponent->keyStateChanged (false); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h000066400000000000000000000245131320201440200334440ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_KEYPRESSMAPPINGSET_H_INCLUDED #define JUCE_KEYPRESSMAPPINGSET_H_INCLUDED //============================================================================== /** Manages and edits a list of keypresses, which it uses to invoke the appropriate command in an ApplicationCommandManager. Normally, you won't actually create a KeyPressMappingSet directly, because each ApplicationCommandManager contains its own KeyPressMappingSet, so typically you'd create yourself an ApplicationCommandManager, and call its ApplicationCommandManager::getKeyMappings() method to get a pointer to its KeyPressMappingSet. For one of these to actually use keypresses, you'll need to add it as a KeyListener to the top-level component for which you want to handle keystrokes. So for example: @code class MyMainWindow : public Component { ApplicationCommandManager* myCommandManager; public: MyMainWindow() { myCommandManager = new ApplicationCommandManager(); // first, make sure the command manager has registered all the commands that its // targets can perform.. myCommandManager->registerAllCommandsForTarget (myCommandTarget1); myCommandManager->registerAllCommandsForTarget (myCommandTarget2); // this will use the command manager to initialise the KeyPressMappingSet with // the default keypresses that were specified when the targets added their commands // to the manager. myCommandManager->getKeyMappings()->resetToDefaultMappings(); // having set up the default key-mappings, you might now want to load the last set // of mappings that the user configured. myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML); // Now tell our top-level window to send any keypresses that arrive to the // KeyPressMappingSet, which will use them to invoke the appropriate commands. addKeyListener (myCommandManager->getKeyMappings()); } ... } @endcode KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can register to be told when a command or mapping is added, removed, etc. There's also a UI component called KeyMappingEditorComponent that can be used to easily edit the key mappings. @see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager */ class JUCE_API KeyPressMappingSet : public KeyListener, public ChangeBroadcaster, private FocusChangeListener { public: //============================================================================== /** Creates a KeyPressMappingSet for a given command manager. Normally, you won't actually create a KeyPressMappingSet directly, because each ApplicationCommandManager contains its own KeyPressMappingSet, so the best thing to do is to create your ApplicationCommandManager, and use the ApplicationCommandManager::getKeyMappings() method to access its mappings. When a suitable keypress happens, the manager's invoke() method will be used to invoke the appropriate command. @see ApplicationCommandManager */ explicit KeyPressMappingSet (ApplicationCommandManager&); /** Creates an copy of a KeyPressMappingSet. */ KeyPressMappingSet (const KeyPressMappingSet&); /** Destructor. */ ~KeyPressMappingSet(); //============================================================================== ApplicationCommandManager& getCommandManager() const noexcept { return commandManager; } //============================================================================== /** Returns a list of keypresses that are assigned to a particular command. @param commandID the command's ID */ Array getKeyPressesAssignedToCommand (CommandID commandID) const; /** Assigns a keypress to a command. If the keypress is already assigned to a different command, it will first be removed from that command, to avoid it triggering multiple functions. @param commandID the ID of the command that you want to add a keypress to. If this is 0, the keypress will be removed from anything that it was previously assigned to, but not re-assigned @param newKeyPress the new key-press @param insertIndex if this is less than zero, the key will be appended to the end of the list of keypresses; otherwise the new keypress will be inserted into the existing list at this index */ void addKeyPress (CommandID commandID, const KeyPress& newKeyPress, int insertIndex = -1); /** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager. @see resetToDefaultMapping */ void resetToDefaultMappings(); /** Resets all key-mappings to the defaults for a particular command. @see resetToDefaultMappings */ void resetToDefaultMapping (CommandID commandID); /** Removes all keypresses that are assigned to any commands. */ void clearAllKeyPresses(); /** Removes all keypresses that are assigned to a particular command. */ void clearAllKeyPresses (CommandID commandID); /** Removes one of the keypresses that are assigned to a command. See the getKeyPressesAssignedToCommand() for the list of keypresses to which the keyPressIndex refers. */ void removeKeyPress (CommandID commandID, int keyPressIndex); /** Removes a keypress from any command that it may be assigned to. */ void removeKeyPress (const KeyPress& keypress); /** Returns true if the given command is linked to this key. */ bool containsMapping (CommandID commandID, const KeyPress& keyPress) const noexcept; //============================================================================== /** Looks for a command that corresponds to a keypress. @returns the UID of the command or 0 if none was found */ CommandID findCommandForKeyPress (const KeyPress& keyPress) const noexcept; //============================================================================== /** Tries to recreate the mappings from a previously stored state. The XML passed in must have been created by the createXml() method. If the stored state makes any reference to commands that aren't currently available, these will be ignored. If the set of mappings being loaded was a set of differences (using createXml (true)), then this will call resetToDefaultMappings() and then merge the saved mappings on top. If the saved set was created with createXml (false), then this method will first clear all existing mappings and load the saved ones as a complete set. @returns true if it manages to load the XML correctly @see createXml */ bool restoreFromXml (const XmlElement& xmlVersion); /** Creates an XML representation of the current mappings. This will produce a lump of XML that can be later reloaded using restoreFromXml() to recreate the current mapping state. The object that is returned must be deleted by the caller. @param saveDifferencesFromDefaultSet if this is false, then all keypresses will be saved into the XML. If it's true, then the XML will only store the differences between the current mappings and the default mappings you'd get from calling resetToDefaultMappings(). The advantage of saving a set of differences from the default is that if you change the default mappings (in a new version of your app, for example), then these will be merged into a user's saved preferences. @see restoreFromXml */ XmlElement* createXml (bool saveDifferencesFromDefaultSet) const; //============================================================================== /** @internal */ bool keyPressed (const KeyPress&, Component*) override; /** @internal */ bool keyStateChanged (bool isKeyDown, Component*) override; /** @internal */ void globalFocusChanged (Component*) override; private: //============================================================================== ApplicationCommandManager& commandManager; struct CommandMapping { CommandID commandID; Array keypresses; bool wantsKeyUpDownCallbacks; }; OwnedArray mappings; struct KeyPressTime { KeyPress key; uint32 timeWhenPressed; }; OwnedArray keysDown; void invokeCommand (const CommandID, const KeyPress&, const bool isKeyDown, const int millisecsSinceKeyPressed, Component* originator) const; KeyPressMappingSet& operator= (const KeyPressMappingSet&); JUCE_LEAK_DETECTOR (KeyPressMappingSet) }; #endif // JUCE_KEYPRESSMAPPINGSET_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/000077500000000000000000000000001320201440200271075ustar00rootroot00000000000000juce_CachedComponentImage.h000066400000000000000000000050171320201440200342070ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED #define JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED //============================================================================== /** Base class used internally for structures that can store cached images of component state. Most people are unlikely to ever need to know about this class - it's really only for power-users! @see Component::setCachedComponentImage */ class JUCE_API CachedComponentImage { public: CachedComponentImage() noexcept {} virtual ~CachedComponentImage() {} //============================================================================== /** Called as part of the parent component's paint method, this must draw the given component into the target graphics context, using the cached version where possible. */ virtual void paint (Graphics&) = 0; /** Invalidates all cached image data. @returns true if the peer should also be repainted, or false if this object handles all repaint work internally. */ virtual bool invalidateAll() = 0; /** Invalidates a section of the cached image data. @returns true if the peer should also be repainted, or false if this object handles all repaint work internally. */ virtual bool invalidate (const Rectangle& area) = 0; /** Called to indicate that the component is no longer active, so any cached data should be released if possible. */ virtual void releaseResources() = 0; }; #endif // JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp000066400000000000000000003005271320201440200325720ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ Component* Component::currentlyFocusedComponent = nullptr; //============================================================================== class Component::MouseListenerList { public: MouseListenerList() noexcept : numDeepMouseListeners (0) { } void addListener (MouseListener* const newListener, const bool wantsEventsForAllNestedChildComponents) { if (! listeners.contains (newListener)) { if (wantsEventsForAllNestedChildComponents) { listeners.insert (0, newListener); ++numDeepMouseListeners; } else { listeners.add (newListener); } } } void removeListener (MouseListener* const listenerToRemove) { const int index = listeners.indexOf (listenerToRemove); if (index >= 0) { if (index < numDeepMouseListeners) --numDeepMouseListeners; listeners.remove (index); } } static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker, void (MouseListener::*eventMethod) (const MouseEvent&), const MouseEvent& e) { if (checker.shouldBailOut()) return; if (MouseListenerList* const list = comp.mouseListeners) { for (int i = list->listeners.size(); --i >= 0;) { (list->listeners.getUnchecked(i)->*eventMethod) (e); if (checker.shouldBailOut()) return; i = jmin (i, list->listeners.size()); } } for (Component* p = comp.parentComponent; p != nullptr; p = p->parentComponent) { MouseListenerList* const list = p->mouseListeners; if (list != nullptr && list->numDeepMouseListeners > 0) { BailOutChecker2 checker2 (checker, p); for (int i = list->numDeepMouseListeners; --i >= 0;) { (list->listeners.getUnchecked(i)->*eventMethod) (e); if (checker2.shouldBailOut()) return; i = jmin (i, list->numDeepMouseListeners); } } } } static void sendWheelEvent (Component& comp, Component::BailOutChecker& checker, const MouseEvent& e, const MouseWheelDetails& wheel) { if (MouseListenerList* const list = comp.mouseListeners) { for (int i = list->listeners.size(); --i >= 0;) { list->listeners.getUnchecked(i)->mouseWheelMove (e, wheel); if (checker.shouldBailOut()) return; i = jmin (i, list->listeners.size()); } } for (Component* p = comp.parentComponent; p != nullptr; p = p->parentComponent) { MouseListenerList* const list = p->mouseListeners; if (list != nullptr && list->numDeepMouseListeners > 0) { BailOutChecker2 checker2 (checker, p); for (int i = list->numDeepMouseListeners; --i >= 0;) { list->listeners.getUnchecked(i)->mouseWheelMove (e, wheel); if (checker2.shouldBailOut()) return; i = jmin (i, list->numDeepMouseListeners); } } } } private: Array listeners; int numDeepMouseListeners; class BailOutChecker2 { public: BailOutChecker2 (Component::BailOutChecker& boc, Component* const comp) : checker (boc), safePointer (comp) { } bool shouldBailOut() const noexcept { return checker.shouldBailOut() || safePointer == nullptr; } private: Component::BailOutChecker& checker; const WeakReference safePointer; JUCE_DECLARE_NON_COPYABLE (BailOutChecker2) }; JUCE_DECLARE_NON_COPYABLE (MouseListenerList) }; //============================================================================== struct FocusRestorer { FocusRestorer() : lastFocus (Component::getCurrentlyFocusedComponent()) {} ~FocusRestorer() { if (lastFocus != nullptr && ! lastFocus->isCurrentlyBlockedByAnotherModalComponent()) lastFocus->grabKeyboardFocus(); } WeakReference lastFocus; JUCE_DECLARE_NON_COPYABLE (FocusRestorer) }; //============================================================================== struct ScalingHelpers { template static PointOrRect unscaledScreenPosToScaled (float scale, PointOrRect pos) noexcept { return scale != 1.0f ? pos / scale : pos; } template static PointOrRect scaledScreenPosToUnscaled (float scale, PointOrRect pos) noexcept { return scale != 1.0f ? pos * scale : pos; } // For these, we need to avoid getSmallestIntegerContainer being used, which causes // judder when moving windows static Rectangle unscaledScreenPosToScaled (float scale, Rectangle pos) noexcept { return scale != 1.0f ? Rectangle (roundToInt (pos.getX() / scale), roundToInt (pos.getY() / scale), roundToInt (pos.getWidth() / scale), roundToInt (pos.getHeight() / scale)) : pos; } static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle pos) noexcept { return scale != 1.0f ? Rectangle (roundToInt (pos.getX() * scale), roundToInt (pos.getY() * scale), roundToInt (pos.getWidth() * scale), roundToInt (pos.getHeight() * scale)) : pos; } template static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept { return unscaledScreenPosToScaled (Desktop::getInstance().getGlobalScaleFactor(), pos); } template static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept { return scaledScreenPosToUnscaled (Desktop::getInstance().getGlobalScaleFactor(), pos); } template static PointOrRect unscaledScreenPosToScaled (const Component& comp, PointOrRect pos) noexcept { return unscaledScreenPosToScaled (comp.getDesktopScaleFactor(), pos); } template static PointOrRect scaledScreenPosToUnscaled (const Component& comp, PointOrRect pos) noexcept { return scaledScreenPosToUnscaled (comp.getDesktopScaleFactor(), pos); } static Point addPosition (Point p, const Component& c) noexcept { return p + c.getPosition(); } static Rectangle addPosition (Rectangle p, const Component& c) noexcept { return p + c.getPosition(); } static Point addPosition (Point p, const Component& c) noexcept { return p + c.getPosition().toFloat(); } static Rectangle addPosition (Rectangle p, const Component& c) noexcept { return p + c.getPosition().toFloat(); } static Point subtractPosition (Point p, const Component& c) noexcept { return p - c.getPosition(); } static Rectangle subtractPosition (Rectangle p, const Component& c) noexcept { return p - c.getPosition(); } static Point subtractPosition (Point p, const Component& c) noexcept { return p - c.getPosition().toFloat(); } static Rectangle subtractPosition (Rectangle p, const Component& c) noexcept { return p - c.getPosition().toFloat(); } }; //============================================================================== struct Component::ComponentHelpers { #if JUCE_MODAL_LOOPS_PERMITTED static void* runModalLoopCallback (void* userData) { return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); } #endif static Identifier getColourPropertyId (int colourId) { char reversedHex[32]; char* t = reversedHex; for (unsigned int v = (unsigned int) colourId;;) { *t++ = "0123456789abcdef" [(int) (v & 15)]; v >>= 4; if (v == 0) break; } char destBuffer[32]; char* dest = destBuffer; memcpy (dest, "jcclr_", 6); dest += 6; while (t > reversedHex) *dest++ = *--t; *dest++ = 0; return destBuffer; } //============================================================================== static inline bool hitTest (Component& comp, Point localPoint) { return isPositiveAndBelow (localPoint.x, comp.getWidth()) && isPositiveAndBelow (localPoint.y, comp.getHeight()) && comp.hitTest (localPoint.x, localPoint.y); } // converts an unscaled position within a peer to the local position within that peer's component template static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept { if (comp.isTransformed()) pos = pos.transformedBy (comp.getTransform().inverted()); return ScalingHelpers::unscaledScreenPosToScaled (comp, pos); } // converts a position within a peer's component to the unscaled position within the peer template static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept { if (comp.isTransformed()) pos = pos.transformedBy (comp.getTransform()); return ScalingHelpers::scaledScreenPosToUnscaled (comp, pos); } template static PointOrRect convertFromParentSpace (const Component& comp, PointOrRect pointInParentSpace) { if (comp.affineTransform != nullptr) pointInParentSpace = pointInParentSpace.transformedBy (comp.affineTransform->inverted()); if (comp.isOnDesktop()) { if (ComponentPeer* peer = comp.getPeer()) pointInParentSpace = ScalingHelpers::unscaledScreenPosToScaled (comp, peer->globalToLocal (ScalingHelpers::scaledScreenPosToUnscaled (pointInParentSpace))); else jassertfalse; } else { pointInParentSpace = ScalingHelpers::subtractPosition (pointInParentSpace, comp); } return pointInParentSpace; } template static PointOrRect convertToParentSpace (const Component& comp, PointOrRect pointInLocalSpace) { if (comp.isOnDesktop()) { if (ComponentPeer* peer = comp.getPeer()) pointInLocalSpace = ScalingHelpers::unscaledScreenPosToScaled (peer->localToGlobal (ScalingHelpers::scaledScreenPosToUnscaled (comp, pointInLocalSpace))); else jassertfalse; } else { pointInLocalSpace = ScalingHelpers::addPosition (pointInLocalSpace, comp); } if (comp.affineTransform != nullptr) pointInLocalSpace = pointInLocalSpace.transformedBy (*comp.affineTransform); return pointInLocalSpace; } template static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, const PointOrRect& coordInParent) { const Component* const directParent = target.getParentComponent(); jassert (directParent != nullptr); if (directParent == parent) return convertFromParentSpace (target, coordInParent); return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent)); } template static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) { while (source != nullptr) { if (source == target) return p; if (source->isParentOf (target)) return convertFromDistantParentSpace (source, *target, p); p = convertToParentSpace (*source, p); source = source->getParentComponent(); } jassert (source == nullptr); if (target == nullptr) return p; const Component* const topLevelComp = target->getTopLevelComponent(); p = convertFromParentSpace (*topLevelComp, p); if (topLevelComp == target) return p; return convertFromDistantParentSpace (topLevelComp, *target, p); } static bool clipObscuredRegions (const Component& comp, Graphics& g, const Rectangle clipRect, Point delta) { bool nothingChanged = true; for (int i = comp.childComponentList.size(); --i >= 0;) { const Component& child = *comp.childComponentList.getUnchecked(i); if (child.isVisible() && ! child.isTransformed()) { const Rectangle newClip (clipRect.getIntersection (child.bounds)); if (! newClip.isEmpty()) { if (child.isOpaque() && child.componentTransparency == 0) { g.excludeClipRegion (newClip + delta); nothingChanged = false; } else { const Point childPos (child.getPosition()); if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta)) nothingChanged = false; } } } } return nothingChanged; } static Rectangle getParentOrMainMonitorBounds (const Component& comp) { if (Component* p = comp.getParentComponent()) return p->getLocalBounds(); return Desktop::getInstance().getDisplays().getMainDisplay().userArea; } }; //============================================================================== Component::Component() noexcept : parentComponent (nullptr), lookAndFeel (nullptr), effect (nullptr), componentFlags (0), componentTransparency (0) { } Component::Component (const String& name) noexcept : componentName (name), parentComponent (nullptr), lookAndFeel (nullptr), effect (nullptr), componentFlags (0), componentTransparency (0) { } Component::~Component() { static_jassert (sizeof (flags) <= sizeof (componentFlags)); componentListeners.call (&ComponentListener::componentBeingDeleted, *this); masterReference.clear(); while (childComponentList.size() > 0) removeChildComponent (childComponentList.size() - 1, false, true); if (parentComponent != nullptr) parentComponent->removeChildComponent (parentComponent->childComponentList.indexOf (this), true, false); else if (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent)) giveAwayFocus (currentlyFocusedComponent != this); if (flags.hasHeavyweightPeerFlag) removeFromDesktop(); // Something has added some children to this component during its destructor! Not a smart idea! jassert (childComponentList.size() == 0); } //============================================================================== void Component::setName (const String& name) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN if (componentName != name) { componentName = name; if (flags.hasHeavyweightPeerFlag) if (ComponentPeer* const peer = getPeer()) peer->setTitle (name); BailOutChecker checker (this); componentListeners.callChecked (checker, &ComponentListener::componentNameChanged, *this); } } void Component::setComponentID (const String& newID) { componentID = newID; } void Component::setVisible (bool shouldBeVisible) { if (flags.visibleFlag != shouldBeVisible) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN const WeakReference safePointer (this); flags.visibleFlag = shouldBeVisible; if (shouldBeVisible) repaint(); else repaintParent(); sendFakeMouseMove(); if (! shouldBeVisible) { if (cachedImage != nullptr) cachedImage->releaseResources(); if (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent)) { if (parentComponent != nullptr) parentComponent->grabKeyboardFocus(); else giveAwayFocus (true); } } if (safePointer != nullptr) { sendVisibilityChangeMessage(); if (safePointer != nullptr && flags.hasHeavyweightPeerFlag) { if (ComponentPeer* const peer = getPeer()) { peer->setVisible (shouldBeVisible); internalHierarchyChanged(); } } } } } void Component::visibilityChanged() {} void Component::sendVisibilityChangeMessage() { BailOutChecker checker (this); visibilityChanged(); if (! checker.shouldBailOut()) componentListeners.callChecked (checker, &ComponentListener::componentVisibilityChanged, *this); } bool Component::isShowing() const { if (! flags.visibleFlag) return false; if (parentComponent != nullptr) return parentComponent->isShowing(); if (const ComponentPeer* const peer = getPeer()) return ! peer->isMinimised(); return false; } //============================================================================== void* Component::getWindowHandle() const { if (const ComponentPeer* const peer = getPeer()) return peer->getNativeHandle(); return nullptr; } //============================================================================== void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED if (isOpaque()) styleWanted &= ~ComponentPeer::windowIsSemiTransparent; else styleWanted |= ComponentPeer::windowIsSemiTransparent; // don't use getPeer(), so that we only get the peer that's specifically // for this comp, and not for one of its parents. ComponentPeer* peer = ComponentPeer::getPeerFor (this); if (peer == nullptr || styleWanted != peer->getStyleFlags()) { const WeakReference safePointer (this); #if JUCE_LINUX // it's wise to give the component a non-zero size before // putting it on the desktop, as X windows get confused by this, and // a (1, 1) minimum size is enforced here. setSize (jmax (1, getWidth()), jmax (1, getHeight())); #endif const Point topLeft (getScreenPosition()); bool wasFullscreen = false; bool wasMinimised = false; ComponentBoundsConstrainer* currentConstrainer = nullptr; Rectangle oldNonFullScreenBounds; int oldRenderingEngine = -1; if (peer != nullptr) { ScopedPointer oldPeerToDelete (peer); wasFullscreen = peer->isFullScreen(); wasMinimised = peer->isMinimised(); currentConstrainer = peer->getConstrainer(); oldNonFullScreenBounds = peer->getNonFullScreenBounds(); oldRenderingEngine = peer->getCurrentRenderingEngine(); flags.hasHeavyweightPeerFlag = false; Desktop::getInstance().removeDesktopComponent (this); internalHierarchyChanged(); // give comps a chance to react to the peer change before the old peer is deleted. if (safePointer == nullptr) return; setTopLeftPosition (topLeft); } if (parentComponent != nullptr) parentComponent->removeChildComponent (this); if (safePointer != nullptr) { flags.hasHeavyweightPeerFlag = true; peer = createNewPeer (styleWanted, nativeWindowToAttachTo); Desktop::getInstance().addDesktopComponent (this); bounds.setPosition (topLeft); peer->updateBounds(); if (oldRenderingEngine >= 0) peer->setCurrentRenderingEngine (oldRenderingEngine); peer->setVisible (isVisible()); peer = ComponentPeer::getPeerFor (this); if (peer == nullptr) return; if (wasFullscreen) { peer->setFullScreen (true); peer->setNonFullScreenBounds (oldNonFullScreenBounds); } if (wasMinimised) peer->setMinimised (true); #if JUCE_WINDOWS if (isAlwaysOnTop()) peer->setAlwaysOnTop (true); #endif peer->setConstrainer (currentConstrainer); repaint(); internalHierarchyChanged(); } } } void Component::removeFromDesktop() { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN if (flags.hasHeavyweightPeerFlag) { ComponentPeer* const peer = ComponentPeer::getPeerFor (this); jassert (peer != nullptr); flags.hasHeavyweightPeerFlag = false; delete peer; Desktop::getInstance().removeDesktopComponent (this); } } bool Component::isOnDesktop() const noexcept { return flags.hasHeavyweightPeerFlag; } ComponentPeer* Component::getPeer() const { if (flags.hasHeavyweightPeerFlag) return ComponentPeer::getPeerFor (this); if (parentComponent == nullptr) return nullptr; return parentComponent->getPeer(); } void Component::userTriedToCloseWindow() { /* This means that the user's trying to get rid of your window with the 'close window' system menu option (on windows) or possibly the task manager - you should really handle this and delete or hide your component in an appropriate way. If you want to ignore the event and don't want to trigger this assertion, just override this method and do nothing. */ jassertfalse; } void Component::minimisationStateChanged (bool) {} float Component::getDesktopScaleFactor() const { return Desktop::getInstance().getGlobalScaleFactor(); } //============================================================================== void Component::setOpaque (const bool shouldBeOpaque) { if (shouldBeOpaque != flags.opaqueFlag) { flags.opaqueFlag = shouldBeOpaque; if (flags.hasHeavyweightPeerFlag) if (const ComponentPeer* const peer = ComponentPeer::getPeerFor (this)) addToDesktop (peer->getStyleFlags()); // recreates the heavyweight window repaint(); } } bool Component::isOpaque() const noexcept { return flags.opaqueFlag; } //============================================================================== class StandardCachedComponentImage : public CachedComponentImage { public: StandardCachedComponentImage (Component& c) noexcept : owner (c), scale (1.0f) {} void paint (Graphics& g) override { scale = g.getInternalContext().getPhysicalPixelScaleFactor(); const Rectangle compBounds (owner.getLocalBounds()); const Rectangle imageBounds (compBounds * scale); if (image.isNull() || image.getBounds() != imageBounds) { image = Image (owner.isOpaque() ? Image::RGB : Image::ARGB, jmax (1, imageBounds.getWidth()), jmax (1, imageBounds.getHeight()), ! owner.isOpaque()); validArea.clear(); } if (! validArea.containsRectangle (compBounds)) { Graphics imG (image); LowLevelGraphicsContext& lg = imG.getInternalContext(); for (const Rectangle* i = validArea.begin(), * const e = validArea.end(); i != e; ++i) lg.excludeClipRectangle (*i); if (! owner.isOpaque()) { lg.setFill (Colours::transparentBlack); lg.fillRect (imageBounds, true); lg.setFill (Colours::black); } lg.addTransform (AffineTransform::scale (scale)); owner.paintEntireComponent (imG, true); } validArea = imageBounds; g.setColour (Colours::black.withAlpha (owner.getAlpha())); g.drawImageTransformed (image, AffineTransform::scale (compBounds.getWidth() / (float) imageBounds.getWidth(), compBounds.getHeight() / (float) imageBounds.getHeight()), false); } bool invalidateAll() override { validArea.clear(); return true; } bool invalidate (const Rectangle& area) override { validArea.subtract (area * scale); return true; } void releaseResources() override { image = Image::null; } private: Image image; RectangleList validArea; Component& owner; float scale; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage) }; void Component::setCachedComponentImage (CachedComponentImage* newCachedImage) { if (cachedImage != newCachedImage) { cachedImage = newCachedImage; repaint(); } } void Component::setBufferedToImage (const bool shouldBeBuffered) { // This assertion means that this component is already using a custom CachedComponentImage, // so by calling setBufferedToImage, you'll be deleting the custom one - this is almost certainly // not what you wanted to happen... If you really do know what you're doing here, and want to // avoid this assertion, just call setCachedComponentImage (nullptr) before setBufferedToImage(). jassert (cachedImage == nullptr || dynamic_cast (cachedImage.get()) != nullptr); if (shouldBeBuffered) { if (cachedImage == nullptr) cachedImage = new StandardCachedComponentImage (*this); } else { cachedImage = nullptr; } } //============================================================================== void Component::reorderChildInternal (const int sourceIndex, const int destIndex) { if (sourceIndex != destIndex) { Component* const c = childComponentList.getUnchecked (sourceIndex); jassert (c != nullptr); c->repaintParent(); childComponentList.move (sourceIndex, destIndex); sendFakeMouseMove(); internalChildrenChanged(); } } void Component::toFront (const bool setAsForeground) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN if (flags.hasHeavyweightPeerFlag) { if (ComponentPeer* const peer = getPeer()) { peer->toFront (setAsForeground); if (setAsForeground && ! hasKeyboardFocus (true)) grabKeyboardFocus(); } } else if (parentComponent != nullptr) { const Array& childList = parentComponent->childComponentList; if (childList.getLast() != this) { const int index = childList.indexOf (this); if (index >= 0) { int insertIndex = -1; if (! flags.alwaysOnTopFlag) { insertIndex = childList.size() - 1; while (insertIndex > 0 && childList.getUnchecked (insertIndex)->isAlwaysOnTop()) --insertIndex; } parentComponent->reorderChildInternal (index, insertIndex); } } if (setAsForeground) { internalBroughtToFront(); grabKeyboardFocus(); } } } void Component::toBehind (Component* const other) { if (other != nullptr && other != this) { // the two components must belong to the same parent.. jassert (parentComponent == other->parentComponent); if (parentComponent != nullptr) { const Array& childList = parentComponent->childComponentList; const int index = childList.indexOf (this); if (index >= 0 && childList [index + 1] != other) { int otherIndex = childList.indexOf (other); if (otherIndex >= 0) { if (index < otherIndex) --otherIndex; parentComponent->reorderChildInternal (index, otherIndex); } } } else if (isOnDesktop()) { jassert (other->isOnDesktop()); if (other->isOnDesktop()) { ComponentPeer* const us = getPeer(); ComponentPeer* const them = other->getPeer(); jassert (us != nullptr && them != nullptr); if (us != nullptr && them != nullptr) us->toBehind (them); } } } } void Component::toBack() { if (isOnDesktop()) { jassertfalse; //xxx need to add this to native window } else if (parentComponent != nullptr) { const Array& childList = parentComponent->childComponentList; if (childList.getFirst() != this) { const int index = childList.indexOf (this); if (index > 0) { int insertIndex = 0; if (flags.alwaysOnTopFlag) while (insertIndex < childList.size() && ! childList.getUnchecked (insertIndex)->isAlwaysOnTop()) ++insertIndex; parentComponent->reorderChildInternal (index, insertIndex); } } } } void Component::setAlwaysOnTop (const bool shouldStayOnTop) { if (shouldStayOnTop != flags.alwaysOnTopFlag) { BailOutChecker checker (this); flags.alwaysOnTopFlag = shouldStayOnTop; if (isOnDesktop()) { if (ComponentPeer* const peer = getPeer()) { if (! peer->setAlwaysOnTop (shouldStayOnTop)) { // some kinds of peer can't change their always-on-top status, so // for these, we'll need to create a new window const int oldFlags = peer->getStyleFlags(); removeFromDesktop(); addToDesktop (oldFlags); } } } if (shouldStayOnTop && ! checker.shouldBailOut()) toFront (false); if (! checker.shouldBailOut()) internalHierarchyChanged(); } } bool Component::isAlwaysOnTop() const noexcept { return flags.alwaysOnTopFlag; } //============================================================================== int Component::proportionOfWidth (const float proportion) const noexcept { return roundToInt (proportion * bounds.getWidth()); } int Component::proportionOfHeight (const float proportion) const noexcept { return roundToInt (proportion * bounds.getHeight()); } int Component::getParentWidth() const noexcept { return parentComponent != nullptr ? parentComponent->getWidth() : getParentMonitorArea().getWidth(); } int Component::getParentHeight() const noexcept { return parentComponent != nullptr ? parentComponent->getHeight() : getParentMonitorArea().getHeight(); } int Component::getScreenX() const { return getScreenPosition().x; } int Component::getScreenY() const { return getScreenPosition().y; } Point Component::getScreenPosition() const { return localPointToGlobal (Point()); } Rectangle Component::getScreenBounds() const { return localAreaToGlobal (getLocalBounds()); } Rectangle Component::getParentMonitorArea() const { return Desktop::getInstance().getDisplays().getDisplayContaining (getScreenBounds().getCentre()).userArea; } Point Component::getLocalPoint (const Component* source, Point point) const { return ComponentHelpers::convertCoordinate (this, source, point); } Point Component::getLocalPoint (const Component* source, Point point) const { return ComponentHelpers::convertCoordinate (this, source, point); } Rectangle Component::getLocalArea (const Component* source, const Rectangle& area) const { return ComponentHelpers::convertCoordinate (this, source, area); } Point Component::localPointToGlobal (Point point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); } Point Component::localPointToGlobal (Point point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); } Rectangle Component::localAreaToGlobal (const Rectangle& area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); } // Deprecated methods... Point Component::relativePositionToGlobal (Point relativePosition) const { return localPointToGlobal (relativePosition); } Point Component::globalPositionToRelative (Point screenPosition) const { return getLocalPoint (nullptr, screenPosition); } Point Component::relativePositionToOtherComponent (const Component* const targetComponent, Point positionRelativeToThis) const { return targetComponent == nullptr ? localPointToGlobal (positionRelativeToThis) : targetComponent->getLocalPoint (this, positionRelativeToThis); } //============================================================================== void Component::setBounds (const int x, const int y, int w, int h) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN if (w < 0) w = 0; if (h < 0) h = 0; const bool wasResized = (getWidth() != w || getHeight() != h); const bool wasMoved = (getX() != x || getY() != y); #if JUCE_DEBUG // It's a very bad idea to try to resize a window during its paint() method! jassert (! (flags.isInsidePaintCall && wasResized && isOnDesktop())); #endif if (wasMoved || wasResized) { const bool showing = isShowing(); if (showing) { // send a fake mouse move to trigger enter/exit messages if needed.. sendFakeMouseMove(); if (! flags.hasHeavyweightPeerFlag) repaintParent(); } bounds.setBounds (x, y, w, h); if (showing) { if (wasResized) repaint(); else if (! flags.hasHeavyweightPeerFlag) repaintParent(); } else if (cachedImage != nullptr) { cachedImage->invalidateAll(); } flags.isMoveCallbackPending = wasMoved; flags.isResizeCallbackPending = wasResized; if (flags.hasHeavyweightPeerFlag) if (ComponentPeer* const peer = getPeer()) peer->updateBounds(); sendMovedResizedMessagesIfPending(); } } void Component::sendMovedResizedMessagesIfPending() { const bool wasMoved = flags.isMoveCallbackPending; const bool wasResized = flags.isResizeCallbackPending; if (wasMoved || wasResized) { flags.isMoveCallbackPending = false; flags.isResizeCallbackPending = false; sendMovedResizedMessages (wasMoved, wasResized); } } void Component::sendMovedResizedMessages (const bool wasMoved, const bool wasResized) { BailOutChecker checker (this); if (wasMoved) { moved(); if (checker.shouldBailOut()) return; } if (wasResized) { resized(); if (checker.shouldBailOut()) return; for (int i = childComponentList.size(); --i >= 0;) { childComponentList.getUnchecked(i)->parentSizeChanged(); if (checker.shouldBailOut()) return; i = jmin (i, childComponentList.size()); } } if (parentComponent != nullptr) parentComponent->childBoundsChanged (this); if (! checker.shouldBailOut()) componentListeners.callChecked (checker, &ComponentListener::componentMovedOrResized, *this, wasMoved, wasResized); } void Component::setSize (const int w, const int h) { setBounds (getX(), getY(), w, h); } void Component::setTopLeftPosition (const int x, const int y) { setBounds (x, y, getWidth(), getHeight()); } void Component::setTopLeftPosition (Point pos) { setBounds (pos.x, pos.y, getWidth(), getHeight()); } void Component::setTopRightPosition (const int x, const int y) { setTopLeftPosition (x - getWidth(), y); } void Component::setBounds (const Rectangle& r) { setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight()); } void Component::setBounds (const RelativeRectangle& newBounds) { newBounds.applyToComponent (*this); } void Component::setBounds (const String& newBoundsExpression) { setBounds (RelativeRectangle (newBoundsExpression)); } void Component::setBoundsRelative (const float x, const float y, const float w, const float h) { const int pw = getParentWidth(); const int ph = getParentHeight(); setBounds (roundToInt (x * pw), roundToInt (y * ph), roundToInt (w * pw), roundToInt (h * ph)); } void Component::setCentrePosition (const int x, const int y) { setTopLeftPosition (x - getWidth() / 2, y - getHeight() / 2); } void Component::setCentreRelative (const float x, const float y) { setCentrePosition (roundToInt (getParentWidth() * x), roundToInt (getParentHeight() * y)); } void Component::centreWithSize (const int width, const int height) { const Rectangle parentArea (ComponentHelpers::getParentOrMainMonitorBounds (*this)); setBounds (parentArea.getCentreX() - width / 2, parentArea.getCentreY() - height / 2, width, height); } void Component::setBoundsInset (const BorderSize& borders) { setBounds (borders.subtractedFrom (ComponentHelpers::getParentOrMainMonitorBounds (*this))); } void Component::setBoundsToFit (int x, int y, int width, int height, Justification justification, const bool onlyReduceInSize) { // it's no good calling this method unless both the component and // target rectangle have a finite size. jassert (getWidth() > 0 && getHeight() > 0 && width > 0 && height > 0); if (getWidth() > 0 && getHeight() > 0 && width > 0 && height > 0) { int newW, newH; if (onlyReduceInSize && getWidth() <= width && getHeight() <= height) { newW = getWidth(); newH = getHeight(); } else { const double imageRatio = getHeight() / (double) getWidth(); const double targetRatio = height / (double) width; if (imageRatio <= targetRatio) { newW = width; newH = jmin (height, roundToInt (newW * imageRatio)); } else { newH = height; newW = jmin (width, roundToInt (newH / imageRatio)); } } if (newW > 0 && newH > 0) setBounds (justification.appliedToRectangle (Rectangle (newW, newH), Rectangle (x, y, width, height))); } } //============================================================================== void Component::setTransform (const AffineTransform& newTransform) { // If you pass in a transform with no inverse, the component will have no dimensions, // and there will be all sorts of maths errors when converting coordinates. jassert (! newTransform.isSingularity()); if (newTransform.isIdentity()) { if (affineTransform != nullptr) { repaint(); affineTransform = nullptr; repaint(); sendMovedResizedMessages (false, false); } } else if (affineTransform == nullptr) { repaint(); affineTransform = new AffineTransform (newTransform); repaint(); sendMovedResizedMessages (false, false); } else if (*affineTransform != newTransform) { repaint(); *affineTransform = newTransform; repaint(); sendMovedResizedMessages (false, false); } } bool Component::isTransformed() const noexcept { return affineTransform != nullptr; } AffineTransform Component::getTransform() const { return affineTransform != nullptr ? *affineTransform : AffineTransform::identity; } //============================================================================== bool Component::hitTest (int x, int y) { if (! flags.ignoresMouseClicksFlag) return true; if (flags.allowChildMouseClicksFlag) { for (int i = childComponentList.size(); --i >= 0;) { Component& child = *childComponentList.getUnchecked (i); if (child.isVisible() && ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point (x, y)))) return true; } } return false; } void Component::setInterceptsMouseClicks (const bool allowClicks, const bool allowClicksOnChildComponents) noexcept { flags.ignoresMouseClicksFlag = ! allowClicks; flags.allowChildMouseClicksFlag = allowClicksOnChildComponents; } void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent, bool& allowsClicksOnChildComponents) const noexcept { allowsClicksOnThisComponent = ! flags.ignoresMouseClicksFlag; allowsClicksOnChildComponents = flags.allowChildMouseClicksFlag; } bool Component::contains (Point point) { if (ComponentHelpers::hitTest (*this, point)) { if (parentComponent != nullptr) return parentComponent->contains (ComponentHelpers::convertToParentSpace (*this, point)); if (flags.hasHeavyweightPeerFlag) if (const ComponentPeer* const peer = getPeer()) return peer->contains (ComponentHelpers::localPositionToRawPeerPos (*this, point), true); } return false; } bool Component::reallyContains (Point point, const bool returnTrueIfWithinAChild) { if (! contains (point)) return false; Component* const top = getTopLevelComponent(); const Component* const compAtPosition = top->getComponentAt (top->getLocalPoint (this, point)); return (compAtPosition == this) || (returnTrueIfWithinAChild && isParentOf (compAtPosition)); } Component* Component::getComponentAt (Point position) { if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position)) { for (int i = childComponentList.size(); --i >= 0;) { Component* child = childComponentList.getUnchecked(i); child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position)); if (child != nullptr) return child; } return this; } return nullptr; } Component* Component::getComponentAt (const int x, const int y) { return getComponentAt (Point (x, y)); } //============================================================================== void Component::addChildComponent (Component& child, int zOrder) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN if (child.parentComponent != this) { if (child.parentComponent != nullptr) child.parentComponent->removeChildComponent (&child); else child.removeFromDesktop(); child.parentComponent = this; if (child.isVisible()) child.repaintParent(); if (! child.isAlwaysOnTop()) { if (zOrder < 0 || zOrder > childComponentList.size()) zOrder = childComponentList.size(); while (zOrder > 0) { if (! childComponentList.getUnchecked (zOrder - 1)->isAlwaysOnTop()) break; --zOrder; } } childComponentList.insert (zOrder, &child); child.internalHierarchyChanged(); internalChildrenChanged(); } } void Component::addAndMakeVisible (Component& child, int zOrder) { child.setVisible (true); addChildComponent (child, zOrder); } void Component::addChildComponent (Component* const child, int zOrder) { if (child != nullptr) addChildComponent (*child, zOrder); } void Component::addAndMakeVisible (Component* const child, int zOrder) { if (child != nullptr) addAndMakeVisible (*child, zOrder); } void Component::addChildAndSetID (Component* const child, const String& childID) { if (child != nullptr) { child->setComponentID (childID); addAndMakeVisible (child); } } void Component::removeChildComponent (Component* const child) { removeChildComponent (childComponentList.indexOf (child), true, true); } Component* Component::removeChildComponent (const int index) { return removeChildComponent (index, true, true); } Component* Component::removeChildComponent (const int index, bool sendParentEvents, const bool sendChildEvents) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN Component* const child = childComponentList [index]; if (child != nullptr) { sendParentEvents = sendParentEvents && child->isShowing(); if (sendParentEvents) { sendFakeMouseMove(); if (child->isVisible()) child->repaintParent(); } childComponentList.remove (index); child->parentComponent = nullptr; if (child->cachedImage != nullptr) child->cachedImage->releaseResources(); // (NB: there are obscure situations where child->isShowing() = false, but it still has the focus) if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent)) { if (sendParentEvents) { const WeakReference thisPointer (this); giveAwayFocus (sendChildEvents || currentlyFocusedComponent != child); if (thisPointer == nullptr) return child; grabKeyboardFocus(); } else { giveAwayFocus (sendChildEvents || currentlyFocusedComponent != child); } } if (sendChildEvents) child->internalHierarchyChanged(); if (sendParentEvents) internalChildrenChanged(); } return child; } //============================================================================== void Component::removeAllChildren() { while (childComponentList.size() > 0) removeChildComponent (childComponentList.size() - 1); } void Component::deleteAllChildren() { while (childComponentList.size() > 0) delete (removeChildComponent (childComponentList.size() - 1)); } int Component::getNumChildComponents() const noexcept { return childComponentList.size(); } Component* Component::getChildComponent (const int index) const noexcept { return childComponentList [index]; } int Component::getIndexOfChildComponent (const Component* const child) const noexcept { return childComponentList.indexOf (const_cast (child)); } Component* Component::findChildWithID (StringRef targetID) const noexcept { for (int i = childComponentList.size(); --i >= 0;) { Component* const c = childComponentList.getUnchecked(i); if (c->componentID == targetID) return c; } return nullptr; } Component* Component::getTopLevelComponent() const noexcept { const Component* comp = this; while (comp->parentComponent != nullptr) comp = comp->parentComponent; return const_cast (comp); } bool Component::isParentOf (const Component* possibleChild) const noexcept { while (possibleChild != nullptr) { possibleChild = possibleChild->parentComponent; if (possibleChild == this) return true; } return false; } //============================================================================== void Component::parentHierarchyChanged() {} void Component::childrenChanged() {} void Component::internalChildrenChanged() { if (componentListeners.isEmpty()) { childrenChanged(); } else { BailOutChecker checker (this); childrenChanged(); if (! checker.shouldBailOut()) componentListeners.callChecked (checker, &ComponentListener::componentChildrenChanged, *this); } } void Component::internalHierarchyChanged() { BailOutChecker checker (this); parentHierarchyChanged(); if (checker.shouldBailOut()) return; componentListeners.callChecked (checker, &ComponentListener::componentParentHierarchyChanged, *this); if (checker.shouldBailOut()) return; for (int i = childComponentList.size(); --i >= 0;) { childComponentList.getUnchecked (i)->internalHierarchyChanged(); if (checker.shouldBailOut()) { // you really shouldn't delete the parent component during a callback telling you // that it's changed.. jassertfalse; return; } i = jmin (i, childComponentList.size()); } } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED int Component::runModalLoop() { if (! MessageManager::getInstance()->isThisTheMessageThread()) { // use a callback so this can be called from non-gui threads return (int) (pointer_sized_int) MessageManager::getInstance() ->callFunctionOnMessageThread (&ComponentHelpers::runModalLoopCallback, this); } if (! isCurrentlyModal()) enterModalState (true); return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent(); } #endif //============================================================================== void Component::enterModalState (const bool shouldTakeKeyboardFocus, ModalComponentManager::Callback* callback, const bool deleteWhenDismissed) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED // Check for an attempt to make a component modal when it already is! // This can cause nasty problems.. jassert (! flags.currentlyModalFlag); if (! isCurrentlyModal()) { ModalComponentManager* const mcm = ModalComponentManager::getInstance(); mcm->startModal (this, deleteWhenDismissed); mcm->attachCallback (this, callback); flags.currentlyModalFlag = true; setVisible (true); if (shouldTakeKeyboardFocus) grabKeyboardFocus(); } } void Component::exitModalState (const int returnValue) { if (flags.currentlyModalFlag) { if (MessageManager::getInstance()->isThisTheMessageThread()) { ModalComponentManager::getInstance()->endModal (this, returnValue); flags.currentlyModalFlag = false; ModalComponentManager::getInstance()->bringModalComponentsToFront(); } else { class ExitModalStateMessage : public CallbackMessage { public: ExitModalStateMessage (Component* const c, const int res) : target (c), result (res) {} void messageCallback() override { if (target.get() != nullptr) // (get() required for VS2003 bug) target->exitModalState (result); } private: WeakReference target; int result; }; (new ExitModalStateMessage (this, returnValue))->post(); } } } bool Component::isCurrentlyModal() const noexcept { return flags.currentlyModalFlag && getCurrentlyModalComponent() == this; } bool Component::isCurrentlyBlockedByAnotherModalComponent() const { Component* const mc = getCurrentlyModalComponent(); return ! (mc == nullptr || mc == this || mc->isParentOf (this) || mc->canModalEventBeSentToComponent (this)); } int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() noexcept { return ModalComponentManager::getInstance()->getNumModalComponents(); } Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) noexcept { return ModalComponentManager::getInstance()->getModalComponent (index); } //============================================================================== void Component::setBroughtToFrontOnMouseClick (const bool shouldBeBroughtToFront) noexcept { flags.bringToFrontOnClickFlag = shouldBeBroughtToFront; } bool Component::isBroughtToFrontOnMouseClick() const noexcept { return flags.bringToFrontOnClickFlag; } //============================================================================== void Component::setMouseCursor (const MouseCursor& newCursor) { if (cursor != newCursor) { cursor = newCursor; if (flags.visibleFlag) updateMouseCursor(); } } MouseCursor Component::getMouseCursor() { return cursor; } void Component::updateMouseCursor() const { Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); } //============================================================================== void Component::setRepaintsOnMouseActivity (const bool shouldRepaint) noexcept { flags.repaintOnMouseActivityFlag = shouldRepaint; } //============================================================================== void Component::setAlpha (const float newAlpha) { const uint8 newIntAlpha = (uint8) (255 - jlimit (0, 255, roundToInt (newAlpha * 255.0))); if (componentTransparency != newIntAlpha) { componentTransparency = newIntAlpha; if (flags.hasHeavyweightPeerFlag) { if (ComponentPeer* const peer = getPeer()) peer->setAlpha (newAlpha); } else { repaint(); } } } float Component::getAlpha() const { return (255 - componentTransparency) / 255.0f; } //============================================================================== void Component::repaint() { internalRepaintUnchecked (getLocalBounds(), true); } void Component::repaint (const int x, const int y, const int w, const int h) { internalRepaint (Rectangle (x, y, w, h)); } void Component::repaint (const Rectangle& area) { internalRepaint (area); } void Component::repaintParent() { if (parentComponent != nullptr) parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, getLocalBounds())); } void Component::internalRepaint (Rectangle area) { area = area.getIntersection (getLocalBounds()); if (! area.isEmpty()) internalRepaintUnchecked (area, false); } void Component::internalRepaintUnchecked (Rectangle area, const bool isEntireComponent) { if (flags.visibleFlag) { if (cachedImage != nullptr) if (! (isEntireComponent ? cachedImage->invalidateAll() : cachedImage->invalidate (area))) return; if (flags.hasHeavyweightPeerFlag) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED if (ComponentPeer* const peer = getPeer()) { // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size const Rectangle peerBounds (peer->getBounds()); const Rectangle scaled (area * Point (peerBounds.getWidth() / (float) getWidth(), peerBounds.getHeight() / (float) getHeight())); peer->repaint (affineTransform != nullptr ? scaled.transformedBy (*affineTransform) : scaled); } } else { if (parentComponent != nullptr) parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, area)); } } } //============================================================================== void Component::paint (Graphics&) { // if your component is marked as opaque, you must implement a paint // method and ensure that its entire area is completely painted. jassert (getBounds().isEmpty() || ! isOpaque()); } void Component::paintOverChildren (Graphics&) { // all painting is done in the subclasses } //============================================================================== void Component::paintWithinParentContext (Graphics& g) { g.setOrigin (getPosition()); if (cachedImage != nullptr) cachedImage->paint (g); else paintEntireComponent (g, false); } void Component::paintComponentAndChildren (Graphics& g) { const Rectangle clipBounds (g.getClipBounds()); if (flags.dontClipGraphicsFlag) { paint (g); } else { g.saveState(); if (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, Point()) || ! g.isClipEmpty()) paint (g); g.restoreState(); } for (int i = 0; i < childComponentList.size(); ++i) { Component& child = *childComponentList.getUnchecked (i); if (child.isVisible()) { if (child.affineTransform != nullptr) { g.saveState(); g.addTransform (*child.affineTransform); if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds())) child.paintWithinParentContext (g); g.restoreState(); } else if (clipBounds.intersects (child.getBounds())) { g.saveState(); if (child.flags.dontClipGraphicsFlag) { child.paintWithinParentContext (g); } else if (g.reduceClipRegion (child.getBounds())) { bool nothingClipped = true; for (int j = i + 1; j < childComponentList.size(); ++j) { const Component& sibling = *childComponentList.getUnchecked (j); if (sibling.flags.opaqueFlag && sibling.isVisible() && sibling.affineTransform == nullptr) { nothingClipped = false; g.excludeClipRegion (sibling.getBounds()); } } if (nothingClipped || ! g.isClipEmpty()) child.paintWithinParentContext (g); } g.restoreState(); } } } g.saveState(); paintOverChildren (g); g.restoreState(); } void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel) { // If sizing a top-level-window and the OS paint message is delivered synchronously // before resized() is called, then we'll invoke the callback here, to make sure // the components inside have had a chance to sort their sizes out.. #if JUCE_DEBUG if (! flags.isInsidePaintCall) // (avoids an assertion in plugins hosted in WaveLab) #endif sendMovedResizedMessagesIfPending(); #if JUCE_DEBUG flags.isInsidePaintCall = true; #endif if (effect != nullptr) { const float scale = g.getInternalContext().getPhysicalPixelScaleFactor(); const Rectangle scaledBounds (getLocalBounds() * scale); Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB, scaledBounds.getWidth(), scaledBounds.getHeight(), ! flags.opaqueFlag); { Graphics g2 (effectImage); g2.addTransform (AffineTransform::scale (scaledBounds.getWidth() / (float) getWidth(), scaledBounds.getHeight() / (float) getHeight())); paintComponentAndChildren (g2); } g.saveState(); g.addTransform (AffineTransform::scale (1.0f / scale)); effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : getAlpha()); g.restoreState(); } else if (componentTransparency > 0 && ! ignoreAlphaLevel) { if (componentTransparency < 255) { g.beginTransparencyLayer (getAlpha()); paintComponentAndChildren (g); g.endTransparencyLayer(); } } else { paintComponentAndChildren (g); } #if JUCE_DEBUG flags.isInsidePaintCall = false; #endif } void Component::setPaintingIsUnclipped (const bool shouldPaintWithoutClipping) noexcept { flags.dontClipGraphicsFlag = shouldPaintWithoutClipping; } //============================================================================== Image Component::createComponentSnapshot (const Rectangle& areaToGrab, bool clipImageToComponentBounds, float scaleFactor) { Rectangle r (areaToGrab); if (clipImageToComponentBounds) r = r.getIntersection (getLocalBounds()); if (r.isEmpty()) return Image(); const int w = roundToInt (scaleFactor * r.getWidth()); const int h = roundToInt (scaleFactor * r.getHeight()); Image image (flags.opaqueFlag ? Image::RGB : Image::ARGB, w, h, true); Graphics g (image); if (w != getWidth() || h != getHeight()) g.addTransform (AffineTransform::scale (w / (float) r.getWidth(), h / (float) r.getHeight())); g.setOrigin (-r.getPosition()); paintEntireComponent (g, true); return image; } void Component::setComponentEffect (ImageEffectFilter* const newEffect) { if (effect != newEffect) { effect = newEffect; repaint(); } } //============================================================================== LookAndFeel& Component::getLookAndFeel() const noexcept { for (const Component* c = this; c != nullptr; c = c->parentComponent) if (c->lookAndFeel != nullptr) return *(c->lookAndFeel); return LookAndFeel::getDefaultLookAndFeel(); } void Component::setLookAndFeel (LookAndFeel* const newLookAndFeel) { if (lookAndFeel != newLookAndFeel) { lookAndFeel = newLookAndFeel; sendLookAndFeelChange(); } } void Component::lookAndFeelChanged() {} void Component::colourChanged() {} void Component::sendLookAndFeelChange() { const WeakReference safePointer (this); repaint(); lookAndFeelChanged(); if (safePointer != nullptr) { colourChanged(); if (safePointer != nullptr) { for (int i = childComponentList.size(); --i >= 0;) { childComponentList.getUnchecked (i)->sendLookAndFeelChange(); if (safePointer == nullptr) return; i = jmin (i, childComponentList.size()); } } } } Colour Component::findColour (const int colourId, const bool inheritFromParent) const { if (const var* const v = properties.getVarPointer (ComponentHelpers::getColourPropertyId (colourId))) return Colour ((uint32) static_cast (*v)); if (inheritFromParent && parentComponent != nullptr && (lookAndFeel == nullptr || ! lookAndFeel->isColourSpecified (colourId))) return parentComponent->findColour (colourId, true); return getLookAndFeel().findColour (colourId); } bool Component::isColourSpecified (const int colourId) const { return properties.contains (ComponentHelpers::getColourPropertyId (colourId)); } void Component::removeColour (const int colourId) { if (properties.remove (ComponentHelpers::getColourPropertyId (colourId))) colourChanged(); } void Component::setColour (const int colourId, Colour colour) { if (properties.set (ComponentHelpers::getColourPropertyId (colourId), (int) colour.getARGB())) colourChanged(); } void Component::copyAllExplicitColoursTo (Component& target) const { bool changed = false; for (int i = properties.size(); --i >= 0;) { const Identifier name (properties.getName(i)); if (name.toString().startsWith ("jcclr_")) if (target.properties.set (name, properties [name])) changed = true; } if (changed) target.colourChanged(); } //============================================================================== MarkerList* Component::getMarkers (bool /*xAxis*/) { return nullptr; } //============================================================================== Component::Positioner::Positioner (Component& c) noexcept : component (c) { } Component::Positioner* Component::getPositioner() const noexcept { return positioner; } void Component::setPositioner (Positioner* newPositioner) { // You can only assign a positioner to the component that it was created for! jassert (newPositioner == nullptr || this == &(newPositioner->getComponent())); positioner = newPositioner; } //============================================================================== Rectangle Component::getLocalBounds() const noexcept { return bounds.withZeroOrigin(); } Rectangle Component::getBoundsInParent() const noexcept { return affineTransform == nullptr ? bounds : bounds.transformedBy (*affineTransform); } //============================================================================== void Component::mouseEnter (const MouseEvent&) {} void Component::mouseExit (const MouseEvent&) {} void Component::mouseDown (const MouseEvent&) {} void Component::mouseUp (const MouseEvent&) {} void Component::mouseDrag (const MouseEvent&) {} void Component::mouseMove (const MouseEvent&) {} void Component::mouseDoubleClick (const MouseEvent&) {} void Component::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel) { // the base class just passes this event up to its parent.. if (parentComponent != nullptr) parentComponent->mouseWheelMove (e.getEventRelativeTo (parentComponent), wheel); } void Component::mouseMagnify (const MouseEvent& e, float magnifyAmount) { // the base class just passes this event up to its parent.. if (parentComponent != nullptr) parentComponent->mouseMagnify (e.getEventRelativeTo (parentComponent), magnifyAmount); } //============================================================================== void Component::resized() {} void Component::moved() {} void Component::childBoundsChanged (Component*) {} void Component::parentSizeChanged() {} void Component::addComponentListener (ComponentListener* const newListener) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS if (getParentComponent() != nullptr) ASSERT_MESSAGE_MANAGER_IS_LOCKED; #endif componentListeners.add (newListener); } void Component::removeComponentListener (ComponentListener* const listenerToRemove) { componentListeners.remove (listenerToRemove); } //============================================================================== void Component::inputAttemptWhenModal() { ModalComponentManager::getInstance()->bringModalComponentsToFront(); getLookAndFeel().playAlertSound(); } bool Component::canModalEventBeSentToComponent (const Component*) { return false; } void Component::internalModalInputAttempt() { if (Component* const current = getCurrentlyModalComponent()) current->inputAttemptWhenModal(); } //============================================================================== void Component::postCommandMessage (const int commandId) { struct CustomCommandMessage : public CallbackMessage { CustomCommandMessage (Component* const c, const int command) : target (c), commandId (command) {} void messageCallback() override { if (Component* c = target.get()) c->handleCommandMessage (commandId); } private: WeakReference target; int commandId; }; (new CustomCommandMessage (this, commandId))->post(); } void Component::handleCommandMessage (int) { // used by subclasses } //============================================================================== void Component::addMouseListener (MouseListener* const newListener, const bool wantsEventsForAllNestedChildComponents) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED // If you register a component as a mouselistener for itself, it'll receive all the events // twice - once via the direct callback that all components get anyway, and then again as a listener! jassert ((newListener != this) || wantsEventsForAllNestedChildComponents); if (mouseListeners == nullptr) mouseListeners = new MouseListenerList(); mouseListeners->addListener (newListener, wantsEventsForAllNestedChildComponents); } void Component::removeMouseListener (MouseListener* const listenerToRemove) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED if (mouseListeners != nullptr) mouseListeners->removeListener (listenerToRemove); } //============================================================================== void Component::internalMouseEnter (MouseInputSource source, Point relativePos, Time time) { if (isCurrentlyBlockedByAnotherModalComponent()) { // if something else is modal, always just show a normal mouse cursor source.showMouseCursor (MouseCursor::NormalCursor); return; } if (flags.repaintOnMouseActivityFlag) repaint(); BailOutChecker checker (this); const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, 0, false); mouseEnter (me); if (checker.shouldBailOut()) return; Desktop::getInstance().getMouseListeners().callChecked (checker, &MouseListener::mouseEnter, me); MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseEnter, me); } void Component::internalMouseExit (MouseInputSource source, Point relativePos, Time time) { if (flags.repaintOnMouseActivityFlag) repaint(); BailOutChecker checker (this); const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, 0, false); mouseExit (me); if (checker.shouldBailOut()) return; Desktop::getInstance().getMouseListeners().callChecked (checker, &MouseListener::mouseExit, me); MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseExit, me); } void Component::internalMouseDown (MouseInputSource source, Point relativePos, Time time) { Desktop& desktop = Desktop::getInstance(); BailOutChecker checker (this); if (isCurrentlyBlockedByAnotherModalComponent()) { flags.mouseDownWasBlocked = true; internalModalInputAttempt(); if (checker.shouldBailOut()) return; // If processing the input attempt has exited the modal loop, we'll allow the event // to be delivered.. if (isCurrentlyBlockedByAnotherModalComponent()) { // allow blocked mouse-events to go to global listeners.. const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, source.getNumberOfMultipleClicks(), false); desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseDown, me); return; } } flags.mouseDownWasBlocked = false; for (Component* c = this; c != nullptr; c = c->parentComponent) { if (c->isBroughtToFrontOnMouseClick()) { c->toFront (true); if (checker.shouldBailOut()) return; } } if (! flags.dontFocusOnMouseClickFlag) { grabFocusInternal (focusChangedByMouseClick, true); if (checker.shouldBailOut()) return; } if (flags.repaintOnMouseActivityFlag) repaint(); const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, source.getNumberOfMultipleClicks(), false); mouseDown (me); if (checker.shouldBailOut()) return; desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseDown, me); MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseDown, me); } void Component::internalMouseUp (MouseInputSource source, Point relativePos, Time time, const ModifierKeys oldModifiers) { if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent()) return; BailOutChecker checker (this); if (flags.repaintOnMouseActivityFlag) repaint(); const MouseEvent me (source, relativePos, oldModifiers, this, this, time, getLocalPoint (nullptr, source.getLastMouseDownPosition()), source.getLastMouseDownTime(), source.getNumberOfMultipleClicks(), source.hasMouseMovedSignificantlySincePressed()); mouseUp (me); if (checker.shouldBailOut()) return; Desktop& desktop = Desktop::getInstance(); desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseUp, me); MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseUp, me); if (checker.shouldBailOut()) return; // check for double-click if (me.getNumberOfClicks() >= 2) { mouseDoubleClick (me); if (checker.shouldBailOut()) return; desktop.mouseListeners.callChecked (checker, &MouseListener::mouseDoubleClick, me); MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseDoubleClick, me); } } void Component::internalMouseDrag (MouseInputSource source, Point relativePos, Time time) { if (! isCurrentlyBlockedByAnotherModalComponent()) { BailOutChecker checker (this); const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, getLocalPoint (nullptr, source.getLastMouseDownPosition()), source.getLastMouseDownTime(), source.getNumberOfMultipleClicks(), source.hasMouseMovedSignificantlySincePressed()); mouseDrag (me); if (checker.shouldBailOut()) return; Desktop::getInstance().getMouseListeners().callChecked (checker, &MouseListener::mouseDrag, me); MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseDrag, me); } } void Component::internalMouseMove (MouseInputSource source, Point relativePos, Time time) { Desktop& desktop = Desktop::getInstance(); if (isCurrentlyBlockedByAnotherModalComponent()) { // allow blocked mouse-events to go to global listeners.. desktop.sendMouseMove(); } else { BailOutChecker checker (this); const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, 0, false); mouseMove (me); if (checker.shouldBailOut()) return; desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseMove, me); MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseMove, me); } } void Component::internalMouseWheel (MouseInputSource source, Point relativePos, Time time, const MouseWheelDetails& wheel) { Desktop& desktop = Desktop::getInstance(); BailOutChecker checker (this); const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, 0, false); if (isCurrentlyBlockedByAnotherModalComponent()) { // allow blocked mouse-events to go to global listeners.. desktop.mouseListeners.callChecked (checker, &MouseListener::mouseWheelMove, me, wheel); } else { mouseWheelMove (me, wheel); if (checker.shouldBailOut()) return; desktop.mouseListeners.callChecked (checker, &MouseListener::mouseWheelMove, me, wheel); if (! checker.shouldBailOut()) MouseListenerList::sendWheelEvent (*this, checker, me, wheel); } } void Component::internalMagnifyGesture (MouseInputSource source, Point relativePos, Time time, float amount) { if (! isCurrentlyBlockedByAnotherModalComponent()) { const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, 0, false); mouseMagnify (me, amount); } } void Component::sendFakeMouseMove() const { MouseInputSource mainMouse = Desktop::getInstance().getMainMouseSource(); if (! mainMouse.isDragging()) mainMouse.triggerFakeMove(); } void JUCE_CALLTYPE Component::beginDragAutoRepeat (const int interval) { Desktop::getInstance().beginDragAutoRepeat (interval); } //============================================================================== void Component::broughtToFront() { } void Component::internalBroughtToFront() { if (flags.hasHeavyweightPeerFlag) Desktop::getInstance().componentBroughtToFront (this); BailOutChecker checker (this); broughtToFront(); if (checker.shouldBailOut()) return; componentListeners.callChecked (checker, &ComponentListener::componentBroughtToFront, *this); if (checker.shouldBailOut()) return; // When brought to the front and there's a modal component blocking this one, // we need to bring the modal one to the front instead.. if (Component* const cm = getCurrentlyModalComponent()) if (cm->getTopLevelComponent() != getTopLevelComponent()) ModalComponentManager::getInstance()->bringModalComponentsToFront (false); // very important that this is false, otherwise in Windows, // non-front components can't get focus when another modal comp is // active, and therefore can't receive mouse-clicks } //============================================================================== void Component::focusGained (FocusChangeType) {} void Component::focusLost (FocusChangeType) {} void Component::focusOfChildComponentChanged (FocusChangeType) {} void Component::internalFocusGain (const FocusChangeType cause) { internalFocusGain (cause, WeakReference (this)); } void Component::internalFocusGain (const FocusChangeType cause, const WeakReference& safePointer) { focusGained (cause); if (safePointer != nullptr) internalChildFocusChange (cause, safePointer); } void Component::internalFocusLoss (const FocusChangeType cause) { const WeakReference safePointer (this); focusLost (focusChangedDirectly); if (safePointer != nullptr) internalChildFocusChange (cause, safePointer); } void Component::internalChildFocusChange (FocusChangeType cause, const WeakReference& safePointer) { const bool childIsNowFocused = hasKeyboardFocus (true); if (flags.childCompFocusedFlag != childIsNowFocused) { flags.childCompFocusedFlag = childIsNowFocused; focusOfChildComponentChanged (cause); if (safePointer == nullptr) return; } if (parentComponent != nullptr) parentComponent->internalChildFocusChange (cause, WeakReference (parentComponent)); } void Component::setWantsKeyboardFocus (const bool wantsFocus) noexcept { flags.wantsFocusFlag = wantsFocus; } void Component::setMouseClickGrabsKeyboardFocus (const bool shouldGrabFocus) { flags.dontFocusOnMouseClickFlag = ! shouldGrabFocus; } bool Component::getMouseClickGrabsKeyboardFocus() const noexcept { return ! flags.dontFocusOnMouseClickFlag; } bool Component::getWantsKeyboardFocus() const noexcept { return flags.wantsFocusFlag && ! flags.isDisabledFlag; } void Component::setFocusContainer (const bool shouldBeFocusContainer) noexcept { flags.isFocusContainerFlag = shouldBeFocusContainer; } bool Component::isFocusContainer() const noexcept { return flags.isFocusContainerFlag; } static const Identifier juce_explicitFocusOrderId ("_jexfo"); int Component::getExplicitFocusOrder() const { return properties [juce_explicitFocusOrderId]; } void Component::setExplicitFocusOrder (const int newFocusOrderIndex) { properties.set (juce_explicitFocusOrderId, newFocusOrderIndex); } KeyboardFocusTraverser* Component::createFocusTraverser() { if (flags.isFocusContainerFlag || parentComponent == nullptr) return new KeyboardFocusTraverser(); return parentComponent->createFocusTraverser(); } void Component::takeKeyboardFocus (const FocusChangeType cause) { // give the focus to this component if (currentlyFocusedComponent != this) { // get the focus onto our desktop window if (ComponentPeer* const peer = getPeer()) { const WeakReference safePointer (this); peer->grabFocus(); if (peer->isFocused() && currentlyFocusedComponent != this) { WeakReference componentLosingFocus (currentlyFocusedComponent); currentlyFocusedComponent = this; Desktop::getInstance().triggerFocusCallback(); // call this after setting currentlyFocusedComponent so that the one that's // losing it has a chance to see where focus is going if (componentLosingFocus != nullptr) componentLosingFocus->internalFocusLoss (cause); if (currentlyFocusedComponent == this) internalFocusGain (cause, safePointer); } } } } void Component::grabFocusInternal (const FocusChangeType cause, const bool canTryParent) { if (isShowing()) { if (flags.wantsFocusFlag && (isEnabled() || parentComponent == nullptr)) { takeKeyboardFocus (cause); } else { if (isParentOf (currentlyFocusedComponent) && currentlyFocusedComponent->isShowing()) { // do nothing if the focused component is actually a child of ours.. } else { // find the default child component.. ScopedPointer traverser (createFocusTraverser()); if (traverser != nullptr) { Component* const defaultComp = traverser->getDefaultComponent (this); traverser = nullptr; if (defaultComp != nullptr) { defaultComp->grabFocusInternal (cause, false); return; } } if (canTryParent && parentComponent != nullptr) { // if no children want it and we're allowed to try our parent comp, // then pass up to parent, which will try our siblings. parentComponent->grabFocusInternal (cause, true); } } } } } void Component::grabKeyboardFocus() { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED grabFocusInternal (focusChangedDirectly, true); } void Component::moveKeyboardFocusToSibling (const bool moveToNext) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. ASSERT_MESSAGE_MANAGER_IS_LOCKED if (parentComponent != nullptr) { ScopedPointer traverser (createFocusTraverser()); if (traverser != nullptr) { Component* const nextComp = moveToNext ? traverser->getNextComponent (this) : traverser->getPreviousComponent (this); traverser = nullptr; if (nextComp != nullptr) { if (nextComp->isCurrentlyBlockedByAnotherModalComponent()) { const WeakReference nextCompPointer (nextComp); internalModalInputAttempt(); if (nextCompPointer == nullptr || nextComp->isCurrentlyBlockedByAnotherModalComponent()) return; } nextComp->grabFocusInternal (focusChangedByTabKey, true); return; } } parentComponent->moveKeyboardFocusToSibling (moveToNext); } } bool Component::hasKeyboardFocus (const bool trueIfChildIsFocused) const { return (currentlyFocusedComponent == this) || (trueIfChildIsFocused && isParentOf (currentlyFocusedComponent)); } Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() noexcept { return currentlyFocusedComponent; } void JUCE_CALLTYPE Component::unfocusAllComponents() { if (Component* c = getCurrentlyFocusedComponent()) c->giveAwayFocus (true); } void Component::giveAwayFocus (const bool sendFocusLossEvent) { Component* const componentLosingFocus = currentlyFocusedComponent; currentlyFocusedComponent = nullptr; if (sendFocusLossEvent && componentLosingFocus != nullptr) componentLosingFocus->internalFocusLoss (focusChangedDirectly); Desktop::getInstance().triggerFocusCallback(); } //============================================================================== bool Component::isEnabled() const noexcept { return (! flags.isDisabledFlag) && (parentComponent == nullptr || parentComponent->isEnabled()); } void Component::setEnabled (const bool shouldBeEnabled) { if (flags.isDisabledFlag == shouldBeEnabled) { flags.isDisabledFlag = ! shouldBeEnabled; // if any parent components are disabled, setting our flag won't make a difference, // so no need to send a change message if (parentComponent == nullptr || parentComponent->isEnabled()) sendEnablementChangeMessage(); } } void Component::enablementChanged() {} void Component::sendEnablementChangeMessage() { const WeakReference safePointer (this); enablementChanged(); if (safePointer == nullptr) return; for (int i = getNumChildComponents(); --i >= 0;) { if (Component* const c = getChildComponent (i)) { c->sendEnablementChangeMessage(); if (safePointer == nullptr) return; } } } //============================================================================== bool Component::isMouseOver (const bool includeChildren) const { const Array& mouseSources = Desktop::getInstance().getMouseSources(); for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi) { Component* const c = mi->getComponentUnderMouse(); if ((c == this || (includeChildren && isParentOf (c))) && c->reallyContains (c->getLocalPoint (nullptr, mi->getScreenPosition()).roundToInt(), false) && (mi->isMouse() || mi->isDragging())) return true; } return false; } bool Component::isMouseButtonDown() const { const Array& mouseSources = Desktop::getInstance().getMouseSources(); for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi) if (mi->isDragging() && mi->getComponentUnderMouse() == this) return true; return false; } bool Component::isMouseOverOrDragging() const { const Array& mouseSources = Desktop::getInstance().getMouseSources(); for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi) if (mi->getComponentUnderMouse() == this && (mi->isMouse() || mi->isDragging())) return true; return false; } bool JUCE_CALLTYPE Component::isMouseButtonDownAnywhere() noexcept { return ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(); } Point Component::getMouseXYRelative() const { return getLocalPoint (nullptr, Desktop::getMousePosition()); } //============================================================================== void Component::addKeyListener (KeyListener* const newListener) { if (keyListeners == nullptr) keyListeners = new Array(); keyListeners->addIfNotAlreadyThere (newListener); } void Component::removeKeyListener (KeyListener* const listenerToRemove) { if (keyListeners != nullptr) keyListeners->removeFirstMatchingValue (listenerToRemove); } bool Component::keyPressed (const KeyPress&) { return false; } bool Component::keyStateChanged (const bool /*isKeyDown*/) { return false; } void Component::modifierKeysChanged (const ModifierKeys& modifiers) { if (parentComponent != nullptr) parentComponent->modifierKeysChanged (modifiers); } void Component::internalModifierKeysChanged() { sendFakeMouseMove(); modifierKeysChanged (ModifierKeys::getCurrentModifiers()); } //============================================================================== Component::BailOutChecker::BailOutChecker (Component* const component) : safePointer (component) { jassert (component != nullptr); } bool Component::BailOutChecker::shouldBailOut() const noexcept { return safePointer == nullptr; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h000066400000000000000000003301141320201440200322320ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_COMPONENT_H_INCLUDED #define JUCE_COMPONENT_H_INCLUDED //============================================================================== /** The base class for all JUCE user-interface objects. */ class JUCE_API Component : public MouseListener { public: //============================================================================== /** Creates a component. To get it to actually appear, you'll also need to: - Either add it to a parent component or use the addToDesktop() method to make it a desktop window - Set its size and position to something sensible - Use setVisible() to make it visible And for it to serve any useful purpose, you'll need to write a subclass of Component or use one of the other types of component from the library. */ Component() noexcept; /** Destructor. Note that when a component is deleted, any child components it contains are NOT automatically deleted. It's your responsibilty to manage their lifespan - you may want to use helper methods like deleteAllChildren(), or less haphazard approaches like using ScopedPointers or normal object aggregation to manage them. If the component being deleted is currently the child of another one, then during deletion, it will be removed from its parent, and the parent will receive a childrenChanged() callback. Any ComponentListener objects that have registered with it will also have their ComponentListener::componentBeingDeleted() methods called. */ virtual ~Component(); //============================================================================== /** Creates a component, setting its name at the same time. @see getName, setName */ explicit Component (const String& componentName) noexcept; /** Returns the name of this component. @see setName */ const String& getName() const noexcept { return componentName; } /** Sets the name of this component. When the name changes, all registered ComponentListeners will receive a ComponentListener::componentNameChanged() callback. @see getName */ virtual void setName (const String& newName); /** Returns the ID string that was set by setComponentID(). @see setComponentID, findChildWithID */ const String& getComponentID() const noexcept { return componentID; } /** Sets the component's ID string. You can retrieve the ID using getComponentID(). @see getComponentID, findChildWithID */ void setComponentID (const String& newID); //============================================================================== /** Makes the component visible or invisible. This method will show or hide the component. Note that components default to being non-visible when first created. Also note that visible components won't be seen unless all their parent components are also visible. This method will call visibilityChanged() and also componentVisibilityChanged() for any component listeners that are interested in this component. @param shouldBeVisible whether to show or hide the component @see isVisible, isShowing, visibilityChanged, ComponentListener::componentVisibilityChanged */ virtual void setVisible (bool shouldBeVisible); /** Tests whether the component is visible or not. this doesn't necessarily tell you whether this comp is actually on the screen because this depends on whether all the parent components are also visible - use isShowing() to find this out. @see isShowing, setVisible */ bool isVisible() const noexcept { return flags.visibleFlag; } /** Called when this component's visibility changes. @see setVisible, isVisible */ virtual void visibilityChanged(); /** Tests whether this component and all its parents are visible. @returns true only if this component and all its parents are visible. @see isVisible */ bool isShowing() const; //============================================================================== /** Makes this component appear as a window on the desktop. Note that before calling this, you should make sure that the component's opacity is set correctly using setOpaque(). If the component is non-opaque, the windowing system will try to create a special transparent window for it, which will generally take a lot more CPU to operate (and might not even be possible on some platforms). If the component is inside a parent component at the time this method is called, it will be first be removed from that parent. Likewise if a component on the desktop is subsequently added to another component, it'll be removed from the desktop. @param windowStyleFlags a combination of the flags specified in the ComponentPeer::StyleFlags enum, which define the window's characteristics. @param nativeWindowToAttachTo this allows an OS object to be passed-in as the window in which the juce component should place itself. On Windows, this would be a HWND, a HIViewRef on the Mac. Not necessarily supported on all platforms, and best left as 0 unless you know what you're doing @see removeFromDesktop, isOnDesktop, userTriedToCloseWindow, getPeer, ComponentPeer::setMinimised, ComponentPeer::StyleFlags, ComponentPeer::getStyleFlags, ComponentPeer::setFullScreen */ virtual void addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo = nullptr); /** If the component is currently showing on the desktop, this will hide it. You can also use setVisible() to hide a desktop window temporarily, but removeFromDesktop() will free any system resources that are being used up. @see addToDesktop, isOnDesktop */ void removeFromDesktop(); /** Returns true if this component is currently showing on the desktop. @see addToDesktop, removeFromDesktop */ bool isOnDesktop() const noexcept; /** Returns the heavyweight window that contains this component. If this component is itself on the desktop, this will return the window object that it is using. Otherwise, it will return the window of its top-level parent component. This may return nullptr if there isn't a desktop component. @see addToDesktop, isOnDesktop */ ComponentPeer* getPeer() const; /** For components on the desktop, this is called if the system wants to close the window. This is a signal that either the user or the system wants the window to close. The default implementation of this method will trigger an assertion to warn you that your component should do something about it, but you can override this to ignore the event if you want. */ virtual void userTriedToCloseWindow(); /** Called for a desktop component which has just been minimised or un-minimised. This will only be called for components on the desktop. @see getPeer, ComponentPeer::setMinimised, ComponentPeer::isMinimised */ virtual void minimisationStateChanged (bool isNowMinimised); /** Returns the default scale factor to use for this component when it is placed on the desktop. The default implementation of this method just returns the value from Desktop::getGlobalScaleFactor(), but it can be overridden if a particular component has different requirements. The method only used if this component is added to the desktop - it has no effect for child components. */ virtual float getDesktopScaleFactor() const; //============================================================================== /** Brings the component to the front of its siblings. If some of the component's siblings have had their 'always-on-top' flag set, then they will still be kept in front of this one (unless of course this one is also 'always-on-top'). @param shouldAlsoGainFocus if true, this will also try to assign keyboard focus to the component (see grabKeyboardFocus() for more details) @see toBack, toBehind, setAlwaysOnTop */ void toFront (bool shouldAlsoGainFocus); /** Changes this component's z-order to be at the back of all its siblings. If the component is set to be 'always-on-top', it will only be moved to the back of the other other 'always-on-top' components. @see toFront, toBehind, setAlwaysOnTop */ void toBack(); /** Changes this component's z-order so that it's just behind another component. @see toFront, toBack */ void toBehind (Component* other); /** Sets whether the component should always be kept at the front of its siblings. @see isAlwaysOnTop */ void setAlwaysOnTop (bool shouldStayOnTop); /** Returns true if this component is set to always stay in front of its siblings. @see setAlwaysOnTop */ bool isAlwaysOnTop() const noexcept; //============================================================================== /** Returns the x coordinate of the component's left edge. This is a distance in pixels from the left edge of the component's parent. Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to its bounding box. */ int getX() const noexcept { return bounds.getX(); } /** Returns the y coordinate of the top of this component. This is a distance in pixels from the top edge of the component's parent. Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to its bounding box. */ int getY() const noexcept { return bounds.getY(); } /** Returns the component's width in pixels. */ int getWidth() const noexcept { return bounds.getWidth(); } /** Returns the component's height in pixels. */ int getHeight() const noexcept { return bounds.getHeight(); } /** Returns the x coordinate of the component's right-hand edge. This is a distance in pixels from the left edge of the component's parent. Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to its bounding box. */ int getRight() const noexcept { return bounds.getRight(); } /** Returns the component's top-left position as a Point. */ Point getPosition() const noexcept { return bounds.getPosition(); } /** Returns the y coordinate of the bottom edge of this component. This is a distance in pixels from the top edge of the component's parent. Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to its bounding box. */ int getBottom() const noexcept { return bounds.getBottom(); } /** Returns this component's bounding box. The rectangle returned is relative to the top-left of the component's parent. Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to its bounding box. */ const Rectangle& getBounds() const noexcept { return bounds; } /** Returns the component's bounds, relative to its own origin. This is like getBounds(), but returns the rectangle in local coordinates, In practice, it'll return a rectangle with position (0, 0), and the same size as this component. */ Rectangle getLocalBounds() const noexcept; /** Returns the area of this component's parent which this component covers. The returned area is relative to the parent's coordinate space. If the component has an affine transform specified, then the resulting area will be the smallest rectangle that fully covers the component's transformed bounding box. If this component has no parent, the return value will simply be the same as getBounds(). */ Rectangle getBoundsInParent() const noexcept; //============================================================================== /** Returns this component's x coordinate relative the screen's top-left origin. @see getX, localPointToGlobal */ int getScreenX() const; /** Returns this component's y coordinate relative the screen's top-left origin. @see getY, localPointToGlobal */ int getScreenY() const; /** Returns the position of this component's top-left corner relative to the screen's top-left. @see getScreenBounds */ Point getScreenPosition() const; /** Returns the bounds of this component, relative to the screen's top-left. @see getScreenPosition */ Rectangle getScreenBounds() const; /** Converts a point to be relative to this component's coordinate space. This takes a point relative to a different component, and returns its position relative to this component. If the sourceComponent parameter is null, the source point is assumed to be a global screen coordinate. */ Point getLocalPoint (const Component* sourceComponent, Point pointRelativeToSourceComponent) const; /** Converts a point to be relative to this component's coordinate space. This takes a point relative to a different component, and returns its position relative to this component. If the sourceComponent parameter is null, the source point is assumed to be a global screen coordinate. */ Point getLocalPoint (const Component* sourceComponent, Point pointRelativeToSourceComponent) const; /** Converts a rectangle to be relative to this component's coordinate space. This takes a rectangle that is relative to a different component, and returns its position relative to this component. If the sourceComponent parameter is null, the source rectangle is assumed to be a screen coordinate. If you've used setTransform() to apply one or more transforms to components, then the source rectangle may not actually be rectanglular when converted to the target space, so in that situation this will return the smallest rectangle that fully contains the transformed area. */ Rectangle getLocalArea (const Component* sourceComponent, const Rectangle& areaRelativeToSourceComponent) const; /** Converts a point relative to this component's top-left into a screen coordinate. @see getLocalPoint, localAreaToGlobal */ Point localPointToGlobal (Point localPoint) const; /** Converts a point relative to this component's top-left into a screen coordinate. @see getLocalPoint, localAreaToGlobal */ Point localPointToGlobal (Point localPoint) const; /** Converts a rectangle from this component's coordinate space to a screen coordinate. If you've used setTransform() to apply one or more transforms to components, then the source rectangle may not actually be rectanglular when converted to the target space, so in that situation this will return the smallest rectangle that fully contains the transformed area. @see getLocalPoint, localPointToGlobal */ Rectangle localAreaToGlobal (const Rectangle& localArea) const; //============================================================================== /** Moves the component to a new position. Changes the component's top-left position (without changing its size). The position is relative to the top-left of the component's parent. If the component actually moves, this method will make a synchronous call to moved(). Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to whatever bounds you set for it. @see setBounds, ComponentListener::componentMovedOrResized */ void setTopLeftPosition (int x, int y); /** Moves the component to a new position. Changes the component's top-left position (without changing its size). The position is relative to the top-left of the component's parent. If the component actually moves, this method will make a synchronous call to moved(). Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to whatever bounds you set for it. @see setBounds, ComponentListener::componentMovedOrResized */ void setTopLeftPosition (Point newTopLeftPosition); /** Moves the component to a new position. Changes the position of the component's top-right corner (keeping it the same size). The position is relative to the top-left of the component's parent. If the component actually moves, this method will make a synchronous call to moved(). Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to whatever bounds you set for it. */ void setTopRightPosition (int x, int y); /** Changes the size of the component. A synchronous call to resized() will be occur if the size actually changes. Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to whatever bounds you set for it. */ void setSize (int newWidth, int newHeight); /** Changes the component's position and size. The coordinates are relative to the top-left of the component's parent, or relative to the origin of the screen if the component is on the desktop. If this method changes the component's top-left position, it will make a synchronous call to moved(). If it changes the size, it will also make a call to resized(). Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to whatever bounds you set for it. @see setTopLeftPosition, setSize, ComponentListener::componentMovedOrResized */ void setBounds (int x, int y, int width, int height); /** Changes the component's position and size. The coordinates are relative to the top-left of the component's parent, or relative to the origin of the screen if the component is on the desktop. If this method changes the component's top-left position, it will make a synchronous call to moved(). If it changes the size, it will also make a call to resized(). Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to whatever bounds you set for it. @see setBounds */ void setBounds (const Rectangle& newBounds); /** Changes the component's position and size. This is similar to the other setBounds() methods, but uses RelativeRectangle::applyToComponent() to set the position, This uses a Component::Positioner to make sure that any dynamic expressions are used in the RelativeRectangle will be automatically re-applied to the component's bounds when the source values change. See RelativeRectangle::applyToComponent() for more details. For the syntax of the expressions that are allowed in the string, see the notes for the RelativeCoordinate class. @see RelativeCoordinate, setBounds, RelativeRectangle::applyToComponent(), Expression */ void setBounds (const RelativeRectangle& newBounds); /** Sets the component's bounds with an expression. The string is parsed as a RelativeRectangle expression - see the notes for Component::setBounds (const RelativeRectangle&) for more information. This method is basically just a shortcut for writing setBounds (RelativeRectangle ("...")) */ void setBounds (const String& newBoundsExpression); /** Changes the component's position and size in terms of fractions of its parent's size. The values are factors of the parent's size, so for example setBoundsRelative (0.2f, 0.2f, 0.5f, 0.5f) would give it half the width and height of the parent, with its top-left position 20% of the way across and down the parent. @see setBounds */ void setBoundsRelative (float proportionalX, float proportionalY, float proportionalWidth, float proportionalHeight); /** Changes the component's position and size based on the amount of space to leave around it. This will position the component within its parent, leaving the specified number of pixels around each edge. @see setBounds */ void setBoundsInset (const BorderSize& borders); /** Positions the component within a given rectangle, keeping its proportions unchanged. If onlyReduceInSize is false, the component will be resized to fill as much of the rectangle as possible without changing its aspect ratio (the component's current size is used to determine its aspect ratio, so a zero-size component won't work here). If onlyReduceInSize is true, it will only be resized if it's too big to fit inside the rectangle. It will then be positioned within the rectangle according to the justification flags specified. @see setBounds */ void setBoundsToFit (int x, int y, int width, int height, Justification justification, bool onlyReduceInSize); /** Changes the position of the component's centre. Leaves the component's size unchanged, but sets the position of its centre relative to its parent's top-left. @see setBounds */ void setCentrePosition (int x, int y); /** Changes the position of the component's centre. Leaves the position unchanged, but positions its centre relative to its parent's size. E.g. setCentreRelative (0.5f, 0.5f) would place it centrally in its parent. */ void setCentreRelative (float x, float y); /** Changes the component's size and centres it within its parent. After changing the size, the component will be moved so that it's centred within its parent. If the component is on the desktop (or has no parent component), then it'll be centred within the main monitor area. */ void centreWithSize (int width, int height); //============================================================================== /** Sets a transform matrix to be applied to this component. If you set a transform for a component, the component's position will be warped by it, relative to the component's parent's top-left origin. This means that the values you pass into setBounds() will no longer reflect the actual area within the parent that the component covers, as the bounds will be transformed and the component will probably end up actually appearing somewhere else within its parent. When using transforms you need to be extremely careful when converting coordinates between the coordinate spaces of different components or the screen - you should always use getLocalPoint(), getLocalArea(), etc to do this, and never just manually add a component's position to a point in order to convert it between different components (but I'm sure you would never have done that anyway...). Currently, transforms are not supported for desktop windows, so the transform will be ignored if you put a component on the desktop. To remove a component's transform, simply pass AffineTransform::identity as the parameter to this method. */ void setTransform (const AffineTransform& transform); /** Returns the transform that is currently being applied to this component. For more details about transforms, see setTransform(). @see setTransform */ AffineTransform getTransform() const; /** Returns true if a non-identity transform is being applied to this component. For more details about transforms, see setTransform(). @see setTransform */ bool isTransformed() const noexcept; //============================================================================== /** Returns a proportion of the component's width. This is a handy equivalent of (getWidth() * proportion). */ int proportionOfWidth (float proportion) const noexcept; /** Returns a proportion of the component's height. This is a handy equivalent of (getHeight() * proportion). */ int proportionOfHeight (float proportion) const noexcept; /** Returns the width of the component's parent. If the component has no parent (i.e. if it's on the desktop), this will return the width of the screen. */ int getParentWidth() const noexcept; /** Returns the height of the component's parent. If the component has no parent (i.e. if it's on the desktop), this will return the height of the screen. */ int getParentHeight() const noexcept; /** Returns the screen coordinates of the monitor that contains this component. If there's only one monitor, this will return its size - if there are multiple monitors, it will return the area of the monitor that contains the component's centre. */ Rectangle getParentMonitorArea() const; //============================================================================== /** Returns the number of child components that this component contains. @see getChildComponent, getIndexOfChildComponent */ int getNumChildComponents() const noexcept; /** Returns one of this component's child components, by it index. The component with index 0 is at the back of the z-order, the one at the front will have index (getNumChildComponents() - 1). If the index is out-of-range, this will return a null pointer. @see getNumChildComponents, getIndexOfChildComponent */ Component* getChildComponent (int index) const noexcept; /** Returns the index of this component in the list of child components. A value of 0 means it is first in the list (i.e. behind all other components). Higher values are further towards the front. Returns -1 if the component passed-in is not a child of this component. @see getNumChildComponents, getChildComponent, addChildComponent, toFront, toBack, toBehind */ int getIndexOfChildComponent (const Component* child) const noexcept; /** Looks for a child component with the specified ID. @see setComponentID, getComponentID */ Component* findChildWithID (StringRef componentID) const noexcept; /** Adds a child component to this one. Adding a child component does not mean that the component will own or delete the child - it's your responsibility to delete the component. Note that it's safe to delete a component without first removing it from its parent - doing so will automatically remove it and send out the appropriate notifications before the deletion completes. If the child is already a child of this component, then no action will be taken, and its z-order will be left unchanged. @param child the new component to add. If the component passed-in is already the child of another component, it'll first be removed from it current parent. @param zOrder The index in the child-list at which this component should be inserted. A value of -1 will insert it in front of the others, 0 is the back. @see removeChildComponent, addAndMakeVisible, addChildAndSetID, getChild, ComponentListener::componentChildrenChanged */ void addChildComponent (Component* child, int zOrder = -1); /** Adds a child component to this one. Adding a child component does not mean that the component will own or delete the child - it's your responsibility to delete the component. Note that it's safe to delete a component without first removing it from its parent - doing so will automatically remove it and send out the appropriate notifications before the deletion completes. If the child is already a child of this component, then no action will be taken, and its z-order will be left unchanged. @param child the new component to add. If the component passed-in is already the child of another component, it'll first be removed from it current parent. @param zOrder The index in the child-list at which this component should be inserted. A value of -1 will insert it in front of the others, 0 is the back. @see removeChildComponent, addAndMakeVisible, addChildAndSetID, getChild, ComponentListener::componentChildrenChanged */ void addChildComponent (Component& child, int zOrder = -1); /** Adds a child component to this one, and also makes the child visible if it isn't already. This is the same as calling setVisible (true) on the child and then addChildComponent(). See addChildComponent() for more details. */ void addAndMakeVisible (Component* child, int zOrder = -1); /** Adds a child component to this one, and also makes the child visible if it isn't already. This is the same as calling setVisible (true) on the child and then addChildComponent(). See addChildComponent() for more details. */ void addAndMakeVisible (Component& child, int zOrder = -1); /** Adds a child component to this one, makes it visible, and sets its component ID. @see addAndMakeVisible, addChildComponent */ void addChildAndSetID (Component* child, const String& componentID); /** Removes one of this component's child-components. If the child passed-in isn't actually a child of this component (either because it's invalid or is the child of a different parent), then no action is taken. Note that removing a child will not delete it! But it's ok to delete a component without first removing it - doing so will automatically remove it and send out the appropriate notifications before the deletion completes. @see addChildComponent, ComponentListener::componentChildrenChanged */ void removeChildComponent (Component* childToRemove); /** Removes one of this component's child-components by index. This will return a pointer to the component that was removed, or null if the index was out-of-range. Note that removing a child will not delete it! But it's ok to delete a component without first removing it - doing so will automatically remove it and send out the appropriate notifications before the deletion completes. @see addChildComponent, ComponentListener::componentChildrenChanged */ Component* removeChildComponent (int childIndexToRemove); /** Removes all this component's children. Note that this won't delete them! To do that, use deleteAllChildren() instead. */ void removeAllChildren(); /** Removes and deletes all of this component's children. My advice is to avoid this method! It's an old function that is only kept here for backwards-compatibility with legacy code, and should be viewed with extreme suspicion by anyone attempting to write modern C++. In almost all cases, it's much smarter to manage the lifetimes of your child components via modern RAII techniques such as simply making them member variables, or using ScopedPointer, OwnedArray, etc to manage their lifetimes appropriately. @see removeAllChildren */ void deleteAllChildren(); /** Returns the component which this component is inside. If this is the highest-level component or hasn't yet been added to a parent, this will return null. */ Component* getParentComponent() const noexcept { return parentComponent; } /** Searches the parent components for a component of a specified class. For example findParentComponentOfClass \() would return the first parent component that can be dynamically cast to a MyComp, or will return nullptr if none of the parents are suitable. */ template TargetClass* findParentComponentOfClass() const { for (Component* p = parentComponent; p != nullptr; p = p->parentComponent) if (TargetClass* const target = dynamic_cast (p)) return target; return nullptr; } /** Returns the highest-level component which contains this one or its parents. This will search upwards in the parent-hierarchy from this component, until it finds the highest one that doesn't have a parent (i.e. is on the desktop or not yet added to a parent), and will return that. */ Component* getTopLevelComponent() const noexcept; /** Checks whether a component is anywhere inside this component or its children. This will recursively check through this component's children to see if the given component is anywhere inside. */ bool isParentOf (const Component* possibleChild) const noexcept; //============================================================================== /** Called to indicate that the component's parents have changed. When a component is added or removed from its parent, this method will be called on all of its children (recursively - so all children of its children will also be called as well). Subclasses can override this if they need to react to this in some way. @see getParentComponent, isShowing, ComponentListener::componentParentHierarchyChanged */ virtual void parentHierarchyChanged(); /** Subclasses can use this callback to be told when children are added or removed, or when their z-order changes. @see parentHierarchyChanged, ComponentListener::componentChildrenChanged */ virtual void childrenChanged(); //============================================================================== /** Tests whether a given point is inside the component. Overriding this method allows you to create components which only intercept mouse-clicks within a user-defined area. This is called to find out whether a particular x, y coordinate is considered to be inside the component or not, and is used by methods such as contains() and getComponentAt() to work out which component the mouse is clicked on. Components with custom shapes will probably want to override it to perform some more complex hit-testing. The default implementation of this method returns either true or false, depending on the value that was set by calling setInterceptsMouseClicks() (true is the default return value). Note that the hit-test region is not related to the opacity with which areas of a component are painted. Applications should never call hitTest() directly - instead use the contains() method, because this will also test for occlusion by the component's parent. Note that for components on the desktop, this method will be ignored, because it's not always possible to implement this behaviour on all platforms. @param x the x coordinate to test, relative to the left hand edge of this component. This value is guaranteed to be greater than or equal to zero, and less than the component's width @param y the y coordinate to test, relative to the top edge of this component. This value is guaranteed to be greater than or equal to zero, and less than the component's height @returns true if the click is considered to be inside the component @see setInterceptsMouseClicks, contains */ virtual bool hitTest (int x, int y); /** Changes the default return value for the hitTest() method. Setting this to false is an easy way to make a component pass its mouse-clicks through to the components behind it. When a component is created, the default setting for this is true. @param allowClicksOnThisComponent if true, hitTest() will always return true; if false, it will return false (or true for child components if allowClicksOnChildComponents is true) @param allowClicksOnChildComponents if this is true and allowClicksOnThisComponent is false, then child components can be clicked on as normal but clicks on this component pass straight through; if this is false and allowClicksOnThisComponent is false, then neither this component nor any child components can be clicked on @see hitTest, getInterceptsMouseClicks */ void setInterceptsMouseClicks (bool allowClicksOnThisComponent, bool allowClicksOnChildComponents) noexcept; /** Retrieves the current state of the mouse-click interception flags. On return, the two parameters are set to the state used in the last call to setInterceptsMouseClicks(). @see setInterceptsMouseClicks */ void getInterceptsMouseClicks (bool& allowsClicksOnThisComponent, bool& allowsClicksOnChildComponents) const noexcept; /** Returns true if a given point lies within this component or one of its children. Never override this method! Use hitTest to create custom hit regions. @param localPoint the coordinate to test, relative to this component's top-left. @returns true if the point is within the component's hit-test area, but only if that part of the component isn't clipped by its parent component. Note that this won't take into account any overlapping sibling components which might be in the way - for that, see reallyContains() @see hitTest, reallyContains, getComponentAt */ bool contains (Point localPoint); /** Returns true if a given point lies in this component, taking any overlapping siblings into account. @param localPoint the coordinate to test, relative to this component's top-left. @param returnTrueIfWithinAChild if the point actually lies within a child of this component, this determines whether that is counted as a hit. @see contains, getComponentAt */ bool reallyContains (Point localPoint, bool returnTrueIfWithinAChild); /** Returns the component at a certain point within this one. @param x the x coordinate to test, relative to this component's left edge. @param y the y coordinate to test, relative to this component's top edge. @returns the component that is at this position - which may be 0, this component, or one of its children. Note that overlapping siblings that might actually be in the way are not taken into account by this method - to account for these, instead call getComponentAt on the top-level parent of this component. @see hitTest, contains, reallyContains */ Component* getComponentAt (int x, int y); /** Returns the component at a certain point within this one. @param position the coordinate to test, relative to this component's top-left. @returns the component that is at this position - which may be 0, this component, or one of its children. Note that overlapping siblings that might actually be in the way are not taken into account by this method - to account for these, instead call getComponentAt on the top-level parent of this component. @see hitTest, contains, reallyContains */ Component* getComponentAt (Point position); //============================================================================== /** Marks the whole component as needing to be redrawn. Calling this will not do any repainting immediately, but will mark the component as 'dirty'. At some point in the near future the operating system will send a paint message, which will redraw all the dirty regions of all components. There's no guarantee about how soon after calling repaint() the redraw will actually happen, and other queued events may be delivered before a redraw is done. If the setBufferedToImage() method has been used to cause this component to use a buffer, the repaint() call will invalidate the cached buffer. If setCachedComponentImage() has been used to provide a custom image cache, that cache will be invalidated appropriately. To redraw just a subsection of the component rather than the whole thing, use the repaint (int, int, int, int) method. @see paint */ void repaint(); /** Marks a subsection of this component as needing to be redrawn. Calling this will not do any repainting immediately, but will mark the given region of the component as 'dirty'. At some point in the near future the operating system will send a paint message, which will redraw all the dirty regions of all components. There's no guarantee about how soon after calling repaint() the redraw will actually happen, and other queued events may be delivered before a redraw is done. The region that is passed in will be clipped to keep it within the bounds of this component. @see repaint() */ void repaint (int x, int y, int width, int height); /** Marks a subsection of this component as needing to be redrawn. Calling this will not do any repainting immediately, but will mark the given region of the component as 'dirty'. At some point in the near future the operating system will send a paint message, which will redraw all the dirty regions of all components. There's no guarantee about how soon after calling repaint() the redraw will actually happen, and other queued events may be delivered before a redraw is done. The region that is passed in will be clipped to keep it within the bounds of this component. @see repaint() */ void repaint (const Rectangle& area); //============================================================================== /** Makes the component use an internal buffer to optimise its redrawing. Setting this flag to true will cause the component to allocate an internal buffer into which it paints itself and all its child components, so that when asked to redraw itself, it can use this buffer rather than actually calling the paint() method. Parts of the buffer are invalidated when repaint() is called on this component or its children. The buffer is then repainted at the next paint() callback. @see repaint, paint, createComponentSnapshot */ void setBufferedToImage (bool shouldBeBuffered); /** Generates a snapshot of part of this component. This will return a new Image, the size of the rectangle specified, containing a snapshot of the specified area of the component and all its children. The image may or may not have an alpha-channel, depending on whether the image is opaque or not. If the clipImageToComponentBounds parameter is true and the area is greater than the size of the component, it'll be clipped. If clipImageToComponentBounds is false then parts of the component beyond its bounds can be drawn. @see paintEntireComponent */ Image createComponentSnapshot (const Rectangle& areaToGrab, bool clipImageToComponentBounds = true, float scaleFactor = 1.0f); /** Draws this component and all its subcomponents onto the specified graphics context. You should very rarely have to use this method, it's simply there in case you need to draw a component with a custom graphics context for some reason, e.g. for creating a snapshot of the component. It calls paint(), paintOverChildren() and recursively calls paintEntireComponent() on its children in order to render the entire tree. The graphics context may be left in an undefined state after this method returns, so you may need to reset it if you're going to use it again. If ignoreAlphaLevel is false, then the component will be drawn with the opacity level specified by getAlpha(); if ignoreAlphaLevel is true, then this will be ignored and an alpha of 1.0 will be used. */ void paintEntireComponent (Graphics& context, bool ignoreAlphaLevel); /** This allows you to indicate that this component doesn't require its graphics context to be clipped when it is being painted. Most people will never need to use this setting, but in situations where you have a very large number of simple components being rendered, and where they are guaranteed never to do any drawing beyond their own boundaries, setting this to true will reduce the overhead involved in clipping the graphics context that gets passed to the component's paint() callback. If you enable this mode, you'll need to make sure your paint method doesn't call anything like Graphics::fillAll(), and doesn't draw beyond the component's bounds, because that'll produce artifacts. Your component also can't have any child components that may be placed beyond its bounds. */ void setPaintingIsUnclipped (bool shouldPaintWithoutClipping) noexcept; //============================================================================== /** Adds an effect filter to alter the component's appearance. When a component has an effect filter set, then this is applied to the results of its paint() method. There are a few preset effects, such as a drop-shadow or glow, but they can be user-defined as well. The effect that is passed in will not be deleted by the component - the caller must take care of deleting it. To remove an effect from a component, pass a null pointer in as the parameter. @see ImageEffectFilter, DropShadowEffect, GlowEffect */ void setComponentEffect (ImageEffectFilter* newEffect); /** Returns the current component effect. @see setComponentEffect */ ImageEffectFilter* getComponentEffect() const noexcept { return effect; } //============================================================================== /** Finds the appropriate look-and-feel to use for this component. If the component hasn't had a look-and-feel explicitly set, this will return the parent's look-and-feel, or just the default one if there's no parent. @see setLookAndFeel, lookAndFeelChanged */ LookAndFeel& getLookAndFeel() const noexcept; /** Sets the look and feel to use for this component. This will also change the look and feel for any child components that haven't had their look set explicitly. The object passed in will not be deleted by the component, so it's the caller's responsibility to manage it. It may be used at any time until this component has been deleted. Calling this method will also invoke the sendLookAndFeelChange() method. @see getLookAndFeel, lookAndFeelChanged */ void setLookAndFeel (LookAndFeel* newLookAndFeel); /** Called to let the component react to a change in the look-and-feel setting. When the look-and-feel is changed for a component, this will be called in all its child components, recursively. It can also be triggered manually by the sendLookAndFeelChange() method, in case an application uses a LookAndFeel class that might have changed internally. @see sendLookAndFeelChange, getLookAndFeel */ virtual void lookAndFeelChanged(); /** Calls the lookAndFeelChanged() method in this component and all its children. This will recurse through the children and their children, calling lookAndFeelChanged() on them all. @see lookAndFeelChanged */ void sendLookAndFeelChange(); //============================================================================== /** Indicates whether any parts of the component might be transparent. Components that always paint all of their contents with solid colour and thus completely cover any components behind them should use this method to tell the repaint system that they are opaque. This information is used to optimise drawing, because it means that objects underneath opaque windows don't need to be painted. By default, components are considered transparent, unless this is used to make it otherwise. @see isOpaque */ void setOpaque (bool shouldBeOpaque); /** Returns true if no parts of this component are transparent. @returns the value that was set by setOpaque, (the default being false) @see setOpaque */ bool isOpaque() const noexcept; //============================================================================== /** Indicates whether the component should be brought to the front when clicked. Setting this flag to true will cause the component to be brought to the front when the mouse is clicked somewhere inside it or its child components. Note that a top-level desktop window might still be brought to the front by the operating system when it's clicked, depending on how the OS works. By default this is set to false. @see setMouseClickGrabsKeyboardFocus */ void setBroughtToFrontOnMouseClick (bool shouldBeBroughtToFront) noexcept; /** Indicates whether the component should be brought to the front when clicked-on. @see setBroughtToFrontOnMouseClick */ bool isBroughtToFrontOnMouseClick() const noexcept; //============================================================================== // Keyboard focus methods /** Sets a flag to indicate whether this component needs keyboard focus or not. By default components aren't actually interested in gaining the focus, but this method can be used to turn this on. See the grabKeyboardFocus() method for details about the way a component is chosen to receive the focus. @see grabKeyboardFocus, getWantsKeyboardFocus */ void setWantsKeyboardFocus (bool wantsFocus) noexcept; /** Returns true if the component is interested in getting keyboard focus. This returns the flag set by setWantsKeyboardFocus(). The default setting is false. @see setWantsKeyboardFocus */ bool getWantsKeyboardFocus() const noexcept; //============================================================================== /** Chooses whether a click on this component automatically grabs the focus. By default this is set to true, but you might want a component which can be focused, but where you don't want the user to be able to affect it directly by clicking. */ void setMouseClickGrabsKeyboardFocus (bool shouldGrabFocus); /** Returns the last value set with setMouseClickGrabsKeyboardFocus(). See setMouseClickGrabsKeyboardFocus() for more info. */ bool getMouseClickGrabsKeyboardFocus() const noexcept; //============================================================================== /** Tries to give keyboard focus to this component. When the user clicks on a component or its grabKeyboardFocus() method is called, the following procedure is used to work out which component should get it: - if the component that was clicked on actually wants focus (as indicated by calling getWantsKeyboardFocus), it gets it. - if the component itself doesn't want focus, it will try to pass it on to whichever of its children is the default component, as determined by KeyboardFocusTraverser::getDefaultComponent() - if none of its children want focus at all, it will pass it up to its parent instead, unless it's a top-level component without a parent, in which case it just takes the focus itself. @see setWantsKeyboardFocus, getWantsKeyboardFocus, hasKeyboardFocus, getCurrentlyFocusedComponent, focusGained, focusLost, keyPressed, keyStateChanged */ void grabKeyboardFocus(); /** Returns true if this component currently has the keyboard focus. @param trueIfChildIsFocused if this is true, then the method returns true if either this component or any of its children (recursively) have the focus. If false, the method only returns true if this component has the focus. @see grabKeyboardFocus, setWantsKeyboardFocus, getCurrentlyFocusedComponent, focusGained, focusLost */ bool hasKeyboardFocus (bool trueIfChildIsFocused) const; /** Returns the component that currently has the keyboard focus. @returns the focused component, or null if nothing is focused. */ static Component* JUCE_CALLTYPE getCurrentlyFocusedComponent() noexcept; /** If any component has keyboard focus, this will defocus it. */ static void JUCE_CALLTYPE unfocusAllComponents(); //============================================================================== /** Tries to move the keyboard focus to one of this component's siblings. This will try to move focus to either the next or previous component. (This is the method that is used when shifting focus by pressing the tab key). Components for which getWantsKeyboardFocus() returns false are not looked at. @param moveToNext if true, the focus will move forwards; if false, it will move backwards @see grabKeyboardFocus, setFocusContainer, setWantsKeyboardFocus */ void moveKeyboardFocusToSibling (bool moveToNext); /** Creates a KeyboardFocusTraverser object to use to determine the logic by which focus should be passed from this component. The default implementation of this method will return a default KeyboardFocusTraverser if this component is a focus container (as determined by the setFocusContainer() method). If the component isn't a focus container, then it will recursively ask its parents for a KeyboardFocusTraverser. If you overrride this to return a custom KeyboardFocusTraverser, then this component and all its sub-components will use the new object to make their focusing decisions. The method should return a new object, which the caller is required to delete when no longer needed. */ virtual KeyboardFocusTraverser* createFocusTraverser(); /** Returns the focus order of this component, if one has been specified. By default components don't have a focus order - in that case, this will return 0. Lower numbers indicate that the component will be earlier in the focus traversal order. To change the order, call setExplicitFocusOrder(). The focus order may be used by the KeyboardFocusTraverser class as part of its algorithm for deciding the order in which components should be traversed. See the KeyboardFocusTraverser class for more details on this. @see moveKeyboardFocusToSibling, createFocusTraverser, KeyboardFocusTraverser */ int getExplicitFocusOrder() const; /** Sets the index used in determining the order in which focusable components should be traversed. A value of 0 or less is taken to mean that no explicit order is wanted, and that traversal should use other factors, like the component's position. @see getExplicitFocusOrder, moveKeyboardFocusToSibling */ void setExplicitFocusOrder (int newFocusOrderIndex); /** Indicates whether this component is a parent for components that can have their focus traversed. This flag is used by the default implementation of the createFocusTraverser() method, which uses the flag to find the first parent component (of the currently focused one) which wants to be a focus container. So using this method to set the flag to 'true' causes this component to act as the top level within which focus is passed around. @see isFocusContainer, createFocusTraverser, moveKeyboardFocusToSibling */ void setFocusContainer (bool shouldBeFocusContainer) noexcept; /** Returns true if this component has been marked as a focus container. See setFocusContainer() for more details. @see setFocusContainer, moveKeyboardFocusToSibling, createFocusTraverser */ bool isFocusContainer() const noexcept; //============================================================================== /** Returns true if the component (and all its parents) are enabled. Components are enabled by default, and can be disabled with setEnabled(). Exactly what difference this makes to the component depends on the type. E.g. buttons and sliders will choose to draw themselves differently, etc. Note that if one of this component's parents is disabled, this will always return false, even if this component itself is enabled. @see setEnabled, enablementChanged */ bool isEnabled() const noexcept; /** Enables or disables this component. Disabling a component will also cause all of its child components to become disabled. Similarly, enabling a component which is inside a disabled parent component won't make any difference until the parent is re-enabled. @see isEnabled, enablementChanged */ void setEnabled (bool shouldBeEnabled); /** Callback to indicate that this component has been enabled or disabled. This can be triggered by one of the component's parent components being enabled or disabled, as well as changes to the component itself. The default implementation of this method does nothing; your class may wish to repaint itself or something when this happens. @see setEnabled, isEnabled */ virtual void enablementChanged(); /** Changes the transparency of this component. When painted, the entire component and all its children will be rendered with this as the overall opacity level, where 0 is completely invisible, and 1.0 is fully opaque (i.e. normal). @see getAlpha */ void setAlpha (float newAlpha); /** Returns the component's current transparancy level. See setAlpha() for more details. */ float getAlpha() const; //============================================================================== /** Changes the mouse cursor shape to use when the mouse is over this component. Note that the cursor set by this method can be overridden by the getMouseCursor method. @see MouseCursor */ void setMouseCursor (const MouseCursor& cursorType); /** Returns the mouse cursor shape to use when the mouse is over this component. The default implementation will return the cursor that was set by setCursor() but can be overridden for more specialised purposes, e.g. returning different cursors depending on the mouse position. @see MouseCursor */ virtual MouseCursor getMouseCursor(); /** Forces the current mouse cursor to be updated. If you're overriding the getMouseCursor() method to control which cursor is displayed, then this will only be checked each time the user moves the mouse. So if you want to force the system to check that the cursor being displayed is up-to-date (even if the mouse is just sitting there), call this method. (If you're changing the cursor using setMouseCursor(), you don't need to bother calling this). */ void updateMouseCursor() const; //============================================================================== /** Components can override this method to draw their content. The paint() method gets called when a region of a component needs redrawing, either because the component's repaint() method has been called, or because something has happened on the screen that means a section of a window needs to be redrawn. Any child components will draw themselves over whatever this method draws. If you need to paint over the top of your child components, you can also implement the paintOverChildren() method to do this. If you want to cause a component to redraw itself, this is done asynchronously - calling the repaint() method marks a region of the component as "dirty", and the paint() method will automatically be called sometime later, by the message thread, to paint any bits that need refreshing. In Juce (and almost all modern UI frameworks), you never redraw something synchronously. You should never need to call this method directly - to take a snapshot of the component you could use createComponentSnapshot() or paintEntireComponent(). @param g the graphics context that must be used to do the drawing operations. @see repaint, paintOverChildren, Graphics */ virtual void paint (Graphics& g); /** Components can override this method to draw over the top of their children. For most drawing operations, it's better to use the normal paint() method, but if you need to overlay something on top of the children, this can be used. @see paint, Graphics */ virtual void paintOverChildren (Graphics& g); //============================================================================== /** Called when the mouse moves inside a component. If the mouse button isn't pressed and the mouse moves over a component, this will be called to let the component react to this. A component will always get a mouseEnter callback before a mouseMove. @param event details about the position and status of the mouse event, including the source component in which it occurred @see mouseEnter, mouseExit, mouseDrag, contains */ virtual void mouseMove (const MouseEvent& event) override; /** Called when the mouse first enters a component. If the mouse button isn't pressed and the mouse moves into a component, this will be called to let the component react to this. When the mouse button is pressed and held down while being moved in or out of a component, no mouseEnter or mouseExit callbacks are made - only mouseDrag messages are sent to the component that the mouse was originally clicked on, until the button is released. @param event details about the position and status of the mouse event, including the source component in which it occurred @see mouseExit, mouseDrag, mouseMove, contains */ virtual void mouseEnter (const MouseEvent& event) override; /** Called when the mouse moves out of a component. This will be called when the mouse moves off the edge of this component. If the mouse button was pressed, and it was then dragged off the edge of the component and released, then this callback will happen when the button is released, after the mouseUp callback. @param event details about the position and status of the mouse event, including the source component in which it occurred @see mouseEnter, mouseDrag, mouseMove, contains */ virtual void mouseExit (const MouseEvent& event) override; /** Called when a mouse button is pressed. The MouseEvent object passed in contains lots of methods for finding out which button was pressed, as well as which modifier keys (e.g. shift, ctrl) were held down at the time. Once a button is held down, the mouseDrag method will be called when the mouse moves, until the button is released. @param event details about the position and status of the mouse event, including the source component in which it occurred @see mouseUp, mouseDrag, mouseDoubleClick, contains */ virtual void mouseDown (const MouseEvent& event) override; /** Called when the mouse is moved while a button is held down. When a mouse button is pressed inside a component, that component receives mouseDrag callbacks each time the mouse moves, even if the mouse strays outside the component's bounds. @param event details about the position and status of the mouse event, including the source component in which it occurred @see mouseDown, mouseUp, mouseMove, contains, setDragRepeatInterval */ virtual void mouseDrag (const MouseEvent& event) override; /** Called when a mouse button is released. A mouseUp callback is sent to the component in which a button was pressed even if the mouse is actually over a different component when the button is released. The MouseEvent object passed in contains lots of methods for finding out which buttons were down just before they were released. @param event details about the position and status of the mouse event, including the source component in which it occurred @see mouseDown, mouseDrag, mouseDoubleClick, contains */ virtual void mouseUp (const MouseEvent& event) override; /** Called when a mouse button has been double-clicked on a component. The MouseEvent object passed in contains lots of methods for finding out which button was pressed, as well as which modifier keys (e.g. shift, ctrl) were held down at the time. @param event details about the position and status of the mouse event, including the source component in which it occurred @see mouseDown, mouseUp */ virtual void mouseDoubleClick (const MouseEvent& event) override; /** Called when the mouse-wheel is moved. This callback is sent to the component that the mouse is over when the wheel is moved. If not overridden, a component will forward this message to its parent, so that parent components can collect mouse-wheel messages that happen to child components which aren't interested in them. (Bear in mind that if you attach a component as a mouse-listener to other components, then those wheel moves will also end up calling this method and being passed up to the parents, which may not be what you intended to happen). @param event details about the mouse event @param wheel details about the mouse wheel movement */ virtual void mouseWheelMove (const MouseEvent& event, const MouseWheelDetails& wheel) override; /** Called when a pinch-to-zoom mouse-gesture is used. If not overridden, a component will forward this message to its parent, so that parent components can collect gesture messages that are unused by child components. @param event details about the mouse event @param scaleFactor a multiplier to indicate by how much the size of the target should be changed. A value of 1.0 would indicate no change, values greater than 1.0 mean it should be enlarged. */ virtual void mouseMagnify (const MouseEvent& event, float scaleFactor); //============================================================================== /** Ensures that a non-stop stream of mouse-drag events will be sent during the current mouse-drag operation. This allows you to make sure that mouseDrag() events are sent continuously, even when the mouse isn't moving. This can be useful for things like auto-scrolling components when the mouse is near an edge. Call this method during a mouseDown() or mouseDrag() callback, specifying the minimum interval between consecutive mouse drag callbacks. The callbacks will continue until the mouse is released, and then the interval will be reset, so you need to make sure it's called every time you begin a drag event. Passing an interval of 0 or less will cancel the auto-repeat. @see mouseDrag, Desktop::beginDragAutoRepeat */ static void JUCE_CALLTYPE beginDragAutoRepeat (int millisecondsBetweenCallbacks); /** Causes automatic repaints when the mouse enters or exits this component. If turned on, then when the mouse enters/exits, or when the button is pressed/released on the component, it will trigger a repaint. This is handy for things like buttons that need to draw themselves differently when the mouse moves over them, and it avoids having to override all the different mouse callbacks and call repaint(). @see mouseEnter, mouseExit, mouseDown, mouseUp */ void setRepaintsOnMouseActivity (bool shouldRepaint) noexcept; /** Registers a listener to be told when mouse events occur in this component. If you need to get informed about mouse events in a component but can't or don't want to override its methods, you can attach any number of listeners to the component, and these will get told about the events in addition to the component's own callbacks being called. Note that a MouseListener can also be attached to more than one component. @param newListener the listener to register @param wantsEventsForAllNestedChildComponents if true, the listener will receive callbacks for events that happen to any child component within this component, including deeply-nested child components. If false, it will only be told about events that this component handles. @see MouseListener, removeMouseListener */ void addMouseListener (MouseListener* newListener, bool wantsEventsForAllNestedChildComponents); /** Deregisters a mouse listener. @see addMouseListener, MouseListener */ void removeMouseListener (MouseListener* listenerToRemove); //============================================================================== /** Adds a listener that wants to hear about keypresses that this component receives. The listeners that are registered with a component are called by its keyPressed() or keyStateChanged() methods (assuming these haven't been overridden to do something else). If you add an object as a key listener, be careful to remove it when the object is deleted, or the component will be left with a dangling pointer. @see keyPressed, keyStateChanged, removeKeyListener */ void addKeyListener (KeyListener* newListener); /** Removes a previously-registered key listener. @see addKeyListener */ void removeKeyListener (KeyListener* listenerToRemove); /** Called when a key is pressed. When a key is pressed, the component that has the keyboard focus will have this method called. Remember that a component will only be given the focus if its setWantsKeyboardFocus() method has been used to enable this. If your implementation returns true, the event will be consumed and not passed on to any other listeners. If it returns false, the key will be passed to any KeyListeners that have been registered with this component. As soon as one of these returns true, the process will stop, but if they all return false, the event will be passed upwards to this component's parent, and so on. The default implementation of this method does nothing and returns false. @see keyStateChanged, getCurrentlyFocusedComponent, addKeyListener */ virtual bool keyPressed (const KeyPress& key); /** Called when a key is pressed or released. Whenever a key on the keyboard is pressed or released (including modifier keys like shift and ctrl), this method will be called on the component that currently has the keyboard focus. Remember that a component will only be given the focus if its setWantsKeyboardFocus() method has been used to enable this. If your implementation returns true, the event will be consumed and not passed on to any other listeners. If it returns false, then any KeyListeners that have been registered with this component will have their keyStateChanged methods called. As soon as one of these returns true, the process will stop, but if they all return false, the event will be passed upwards to this component's parent, and so on. The default implementation of this method does nothing and returns false. To find out which keys are up or down at any time, see the KeyPress::isKeyCurrentlyDown() method. @param isKeyDown true if a key has been pressed; false if it has been released @see keyPressed, KeyPress, getCurrentlyFocusedComponent, addKeyListener */ virtual bool keyStateChanged (bool isKeyDown); /** Called when a modifier key is pressed or released. Whenever the shift, control, alt or command keys are pressed or released, this method will be called on the component that currently has the keyboard focus. Remember that a component will only be given the focus if its setWantsKeyboardFocus() method has been used to enable this. The default implementation of this method actually calls its parent's modifierKeysChanged method, so that focused components which aren't interested in this will give their parents a chance to act on the event instead. @see keyStateChanged, ModifierKeys */ virtual void modifierKeysChanged (const ModifierKeys& modifiers); //============================================================================== /** Enumeration used by the focusChanged() and focusLost() methods. */ enum FocusChangeType { focusChangedByMouseClick, /**< Means that the user clicked the mouse to change focus. */ focusChangedByTabKey, /**< Means that the user pressed the tab key to move the focus. */ focusChangedDirectly /**< Means that the focus was changed by a call to grabKeyboardFocus(). */ }; /** Called to indicate that this component has just acquired the keyboard focus. @see focusLost, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus */ virtual void focusGained (FocusChangeType cause); /** Called to indicate that this component has just lost the keyboard focus. @see focusGained, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus */ virtual void focusLost (FocusChangeType cause); /** Called to indicate a change in whether or not this component is the parent of the currently-focused component. Essentially this is called when the return value of a call to hasKeyboardFocus (true) has changed. It happens when focus moves from one of this component's children (at any depth) to a component that isn't contained in this one, (or vice-versa). Note that this method does NOT get called to when focus simply moves from one of its child components to another. @see focusGained, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus */ virtual void focusOfChildComponentChanged (FocusChangeType cause); //============================================================================== /** Returns true if the mouse is currently over this component. If the mouse isn't over the component, this will return false, even if the mouse is currently being dragged - so you can use this in your mouseDrag method to find out whether it's really over the component or not. Note that when the mouse button is being held down, then the only component for which this method will return true is the one that was originally clicked on. If includeChildren is true, then this will also return true if the mouse is over any of the component's children (recursively) as well as the component itself. @see isMouseButtonDown. isMouseOverOrDragging, mouseDrag */ bool isMouseOver (bool includeChildren = false) const; /** Returns true if the mouse button is currently held down in this component. Note that this is a test to see whether the mouse is being pressed in this component, so it'll return false if called on component A when the mouse is actually being dragged in component B. @see isMouseButtonDownAnywhere, isMouseOver, isMouseOverOrDragging */ bool isMouseButtonDown() const; /** True if the mouse is over this component, or if it's being dragged in this component. This is a handy equivalent to (isMouseOver() || isMouseButtonDown()). @see isMouseOver, isMouseButtonDown, isMouseButtonDownAnywhere */ bool isMouseOverOrDragging() const; /** Returns true if a mouse button is currently down. Unlike isMouseButtonDown, this will test the current state of the buttons without regard to which component (if any) it has been pressed in. @see isMouseButtonDown, ModifierKeys */ static bool JUCE_CALLTYPE isMouseButtonDownAnywhere() noexcept; /** Returns the mouse's current position, relative to this component. The return value is relative to the component's top-left corner. */ Point getMouseXYRelative() const; //============================================================================== /** Called when this component's size has been changed. A component can implement this method to do things such as laying out its child components when its width or height changes. The method is called synchronously as a result of the setBounds or setSize methods, so repeatedly changing a components size will repeatedly call its resized method (unlike things like repainting, where multiple calls to repaint are coalesced together). If the component is a top-level window on the desktop, its size could also be changed by operating-system factors beyond the application's control. @see moved, setSize */ virtual void resized(); /** Called when this component's position has been changed. This is called when the position relative to its parent changes, not when its absolute position on the screen changes (so it won't be called for all child components when a parent component is moved). The method is called synchronously as a result of the setBounds, setTopLeftPosition or any of the other repositioning methods, and like resized(), it will be called each time those methods are called. If the component is a top-level window on the desktop, its position could also be changed by operating-system factors beyond the application's control. @see resized, setBounds */ virtual void moved(); /** Called when one of this component's children is moved or resized. If the parent wants to know about changes to its immediate children (not to children of its children), this is the method to override. @see moved, resized, parentSizeChanged */ virtual void childBoundsChanged (Component* child); /** Called when this component's immediate parent has been resized. If the component is a top-level window, this indicates that the screen size has changed. @see childBoundsChanged, moved, resized */ virtual void parentSizeChanged(); /** Called when this component has been moved to the front of its siblings. The component may have been brought to the front by the toFront() method, or by the operating system if it's a top-level window. @see toFront */ virtual void broughtToFront(); /** Adds a listener to be told about changes to the component hierarchy or position. Component listeners get called when this component's size, position or children change - see the ComponentListener class for more details. @param newListener the listener to register - if this is already registered, it will be ignored. @see ComponentListener, removeComponentListener */ void addComponentListener (ComponentListener* newListener); /** Removes a component listener. @see addComponentListener */ void removeComponentListener (ComponentListener* listenerToRemove); //============================================================================== /** Dispatches a numbered message to this component. This is a quick and cheap way of allowing simple asynchronous messages to be sent to components. It's also safe, because if the component that you send the message to is a null or dangling pointer, this won't cause an error. The command ID is later delivered to the component's handleCommandMessage() method by the application's message queue. @see handleCommandMessage */ void postCommandMessage (int commandId); /** Called to handle a command that was sent by postCommandMessage(). This is called by the message thread when a command message arrives, and the component can override this method to process it in any way it needs to. @see postCommandMessage */ virtual void handleCommandMessage (int commandId); //============================================================================== /** Runs a component modally, waiting until the loop terminates. This method first makes the component visible, brings it to the front and gives it the keyboard focus. It then runs a loop, dispatching messages from the system message queue, but blocking all mouse or keyboard messages from reaching any components other than this one and its children. This loop continues until the component's exitModalState() method is called (or the component is deleted), and then this method returns, returning the value passed into exitModalState(). @see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent, isCurrentlyBlockedByAnotherModalComponent, ModalComponentManager */ #if JUCE_MODAL_LOOPS_PERMITTED int runModalLoop(); #endif /** Puts the component into a modal state. This makes the component modal, so that messages are blocked from reaching any components other than this one and its children, but unlike runModalLoop(), this method returns immediately. If takeKeyboardFocus is true, the component will use grabKeyboardFocus() to get the focus, which is usually what you'll want it to do. If not, it will leave the focus unchanged. The callback is an optional object which will receive a callback when the modal component loses its modal status, either by being hidden or when exitModalState() is called. If you pass an object in here, the system will take care of deleting it later, after making the callback If deleteWhenDismissed is true, then when it is dismissed, the component will be deleted and then the callback will be called. (This will safely handle the situation where the component is deleted before its exitModalState() method is called). @see exitModalState, runModalLoop, ModalComponentManager::attachCallback */ void enterModalState (bool takeKeyboardFocus = true, ModalComponentManager::Callback* callback = nullptr, bool deleteWhenDismissed = false); /** Ends a component's modal state. If this component is currently modal, this will turn off its modalness, and return a value to the runModalLoop() method that might have be running its modal loop. @see runModalLoop, enterModalState, isCurrentlyModal */ void exitModalState (int returnValue); /** Returns true if this component is the modal one. It's possible to have nested modal components, e.g. a pop-up dialog box that launches another pop-up, but this will only return true for the one at the top of the stack. @see getCurrentlyModalComponent */ bool isCurrentlyModal() const noexcept; /** Returns the number of components that are currently in a modal state. @see getCurrentlyModalComponent */ static int JUCE_CALLTYPE getNumCurrentlyModalComponents() noexcept; /** Returns one of the components that are currently modal. The index specifies which of the possible modal components to return. The order of the components in this list is the reverse of the order in which they became modal - so the component at index 0 is always the active component, and the others are progressively earlier ones that are themselves now blocked by later ones. @returns the modal component, or null if no components are modal (or if the index is out of range) @see getNumCurrentlyModalComponents, runModalLoop, isCurrentlyModal */ static Component* JUCE_CALLTYPE getCurrentlyModalComponent (int index = 0) noexcept; /** Checks whether there's a modal component somewhere that's stopping this one from receiving messages. If there is a modal component, its canModalEventBeSentToComponent() method will be called to see if it will still allow this component to receive events. @see runModalLoop, getCurrentlyModalComponent */ bool isCurrentlyBlockedByAnotherModalComponent() const; /** When a component is modal, this callback allows it to choose which other components can still receive events. When a modal component is active and the user clicks on a non-modal component, this method is called on the modal component, and if it returns true, the event is allowed to reach its target. If it returns false, the event is blocked and the inputAttemptWhenModal() callback is made. It called by the isCurrentlyBlockedByAnotherModalComponent() method. The default implementation just returns false in all cases. */ virtual bool canModalEventBeSentToComponent (const Component* targetComponent); /** Called when the user tries to click on a component that is blocked by another modal component. When a component is modal and the user clicks on one of the other components, the modal component will receive this callback. The default implementation of this method will play a beep, and bring the currently modal component to the front, but it can be overridden to do other tasks. @see isCurrentlyBlockedByAnotherModalComponent, canModalEventBeSentToComponent */ virtual void inputAttemptWhenModal(); //============================================================================== /** Returns the set of properties that belong to this component. Each component has a NamedValueSet object which you can use to attach arbitrary items of data to it. */ NamedValueSet& getProperties() noexcept { return properties; } /** Returns the set of properties that belong to this component. Each component has a NamedValueSet object which you can use to attach arbitrary items of data to it. */ const NamedValueSet& getProperties() const noexcept { return properties; } //============================================================================== /** Looks for a colour that has been registered with the given colour ID number. If a colour has been set for this ID number using setColour(), then it is returned. If none has been set, the method will try calling the component's LookAndFeel class's findColour() method. If none has been registered with the look-and-feel either, it will just return black. The colour IDs for various purposes are stored as enums in the components that they are relevent to - for an example, see Slider::ColourIds, Label::ColourIds, TextEditor::ColourIds, TreeView::ColourIds, etc. @see setColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour */ Colour findColour (int colourId, bool inheritFromParent = false) const; /** Registers a colour to be used for a particular purpose. Changing a colour will cause a synchronous callback to the colourChanged() method, which your component can override if it needs to do something when colours are altered. For more details about colour IDs, see the comments for findColour(). @see findColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour */ void setColour (int colourId, Colour newColour); /** If a colour has been set with setColour(), this will remove it. This allows you to make a colour revert to its default state. */ void removeColour (int colourId); /** Returns true if the specified colour ID has been explicitly set for this component using the setColour() method. */ bool isColourSpecified (int colourId) const; /** This looks for any colours that have been specified for this component, and copies them to the specified target component. */ void copyAllExplicitColoursTo (Component& target) const; /** This method is called when a colour is changed by the setColour() method. @see setColour, findColour */ virtual void colourChanged(); //============================================================================== /** Components can implement this method to provide a MarkerList. The default implementation of this method returns nullptr, but you can override it to return a pointer to the component's marker list. If xAxis is true, it should return the X marker list; if false, it should return the Y markers. */ virtual MarkerList* getMarkers (bool xAxis); //============================================================================== /** Returns the underlying native window handle for this component. This is platform-dependent and strictly for power-users only! */ void* getWindowHandle() const; //============================================================================== /** Holds a pointer to some type of Component, which automatically becomes null if the component is deleted. If you're using a component which may be deleted by another event that's outside of your control, use a SafePointer instead of a normal pointer to refer to it, and you can test whether it's null before using it to see if something has deleted it. The ComponentType typedef must be Component, or some subclass of Component. You may also want to use a WeakReference object for the same purpose. */ template class SafePointer { public: /** Creates a null SafePointer. */ SafePointer() noexcept {} /** Creates a SafePointer that points at the given component. */ SafePointer (ComponentType* component) : weakRef (component) {} /** Creates a copy of another SafePointer. */ SafePointer (const SafePointer& other) noexcept : weakRef (other.weakRef) {} /** Copies another pointer to this one. */ SafePointer& operator= (const SafePointer& other) { weakRef = other.weakRef; return *this; } /** Copies another pointer to this one. */ SafePointer& operator= (ComponentType* newComponent) { weakRef = newComponent; return *this; } /** Returns the component that this pointer refers to, or null if the component no longer exists. */ ComponentType* getComponent() const noexcept { return dynamic_cast (weakRef.get()); } /** Returns the component that this pointer refers to, or null if the component no longer exists. */ operator ComponentType*() const noexcept { return getComponent(); } /** Returns the component that this pointer refers to, or null if the component no longer exists. */ ComponentType* operator->() noexcept { return getComponent(); } /** Returns the component that this pointer refers to, or null if the component no longer exists. */ const ComponentType* operator->() const noexcept { return getComponent(); } /** If the component is valid, this deletes it and sets this pointer to null. */ void deleteAndZero() { delete getComponent(); } bool operator== (ComponentType* component) const noexcept { return weakRef == component; } bool operator!= (ComponentType* component) const noexcept { return weakRef != component; } private: WeakReference weakRef; }; //============================================================================== /** A class to keep an eye on a component and check for it being deleted. This is designed for use with the ListenerList::callChecked() methods, to allow the list iterator to stop cleanly if the component is deleted by a listener callback while the list is still being iterated. */ class JUCE_API BailOutChecker { public: /** Creates a checker that watches one component. */ BailOutChecker (Component* component); /** Returns true if either of the two components have been deleted since this object was created. */ bool shouldBailOut() const noexcept; private: const WeakReference safePointer; JUCE_DECLARE_NON_COPYABLE (BailOutChecker) }; //============================================================================== /** Base class for objects that can be used to automatically position a component according to some kind of algorithm. The component class simply holds onto a reference to a Positioner, but doesn't actually do anything with it - all the functionality must be implemented by the positioner itself (e.g. it might choose to watch some kind of value and move the component when the value changes). */ class JUCE_API Positioner { public: /** Creates a Positioner which can control the specified component. */ explicit Positioner (Component& component) noexcept; /** Destructor. */ virtual ~Positioner() {} /** Returns the component that this positioner controls. */ Component& getComponent() const noexcept { return component; } /** Attempts to set the component's position to the given rectangle. Unlike simply calling Component::setBounds(), this may involve the positioner being smart enough to adjust itself to fit the new bounds, e.g. a RelativeRectangle's positioner may try to reverse the expressions used to make them fit these new coordinates. */ virtual void applyNewBounds (const Rectangle& newBounds) = 0; private: Component& component; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner) }; /** Returns the Positioner object that has been set for this component. @see setPositioner() */ Positioner* getPositioner() const noexcept; /** Sets a new Positioner object for this component. If there's currently another positioner set, it will be deleted. The object that is passed in will be deleted automatically by this component when it's no longer required. Pass a null pointer to clear the current positioner. @see getPositioner() */ void setPositioner (Positioner* newPositioner); /** Gives the component a CachedComponentImage that should be used to buffer its painting. The object that is passed-in will be owned by this component, and will be deleted automatically later on. @see setBufferedToImage */ void setCachedComponentImage (CachedComponentImage* newCachedImage); /** Returns the object that was set by setCachedComponentImage(). @see setCachedComponentImage */ CachedComponentImage* getCachedComponentImage() const noexcept { return cachedImage; } //============================================================================== // These methods are deprecated - use localPointToGlobal, getLocalPoint, getLocalPoint, etc instead. JUCE_DEPRECATED (Point relativePositionToGlobal (Point) const); JUCE_DEPRECATED (Point globalPositionToRelative (Point) const); JUCE_DEPRECATED (Point relativePositionToOtherComponent (const Component*, Point) const); private: //============================================================================== friend class ComponentPeer; friend class MouseInputSource; friend class MouseInputSourceInternal; #ifndef DOXYGEN static Component* currentlyFocusedComponent; //============================================================================== String componentName, componentID; Component* parentComponent; Rectangle bounds; ScopedPointer positioner; ScopedPointer affineTransform; Array childComponentList; LookAndFeel* lookAndFeel; MouseCursor cursor; ImageEffectFilter* effect; ScopedPointer cachedImage; class MouseListenerList; friend class MouseListenerList; friend struct ContainerDeletePolicy; ScopedPointer mouseListeners; ScopedPointer > keyListeners; ListenerList componentListeners; NamedValueSet properties; friend class WeakReference; WeakReference::Master masterReference; struct ComponentFlags { bool hasHeavyweightPeerFlag : 1; bool visibleFlag : 1; bool opaqueFlag : 1; bool ignoresMouseClicksFlag : 1; bool allowChildMouseClicksFlag : 1; bool wantsFocusFlag : 1; bool isFocusContainerFlag : 1; bool dontFocusOnMouseClickFlag : 1; bool alwaysOnTopFlag : 1; bool bufferToImageFlag : 1; bool bringToFrontOnClickFlag : 1; bool repaintOnMouseActivityFlag : 1; bool currentlyModalFlag : 1; bool isDisabledFlag : 1; bool childCompFocusedFlag : 1; bool dontClipGraphicsFlag : 1; bool mouseDownWasBlocked : 1; bool isMoveCallbackPending : 1; bool isResizeCallbackPending : 1; #if JUCE_DEBUG bool isInsidePaintCall : 1; #endif }; union { uint32 componentFlags; ComponentFlags flags; }; uint8 componentTransparency; //============================================================================== void internalMouseEnter (MouseInputSource, Point, Time); void internalMouseExit (MouseInputSource, Point, Time); void internalMouseDown (MouseInputSource, Point, Time); void internalMouseUp (MouseInputSource, Point, Time, const ModifierKeys oldModifiers); void internalMouseDrag (MouseInputSource, Point, Time); void internalMouseMove (MouseInputSource, Point, Time); void internalMouseWheel (MouseInputSource, Point, Time, const MouseWheelDetails&); void internalMagnifyGesture (MouseInputSource, Point, Time, float); void internalBroughtToFront(); void internalFocusGain (FocusChangeType, const WeakReference&); void internalFocusGain (FocusChangeType); void internalFocusLoss (FocusChangeType); void internalChildFocusChange (FocusChangeType, const WeakReference&); void internalModalInputAttempt(); void internalModifierKeysChanged(); void internalChildrenChanged(); void internalHierarchyChanged(); void internalRepaint (Rectangle); void internalRepaintUnchecked (Rectangle, bool); Component* removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents); void reorderChildInternal (int sourceIndex, int destIndex); void paintComponentAndChildren (Graphics&); void paintWithinParentContext (Graphics&); void sendMovedResizedMessages (bool wasMoved, bool wasResized); void sendMovedResizedMessagesIfPending(); void repaintParent(); void sendFakeMouseMove() const; void takeKeyboardFocus (const FocusChangeType); void grabFocusInternal (const FocusChangeType, bool canTryParent); static void giveAwayFocus (bool sendFocusLossEvent); void sendEnablementChangeMessage(); void sendVisibilityChangeMessage(); struct ComponentHelpers; friend struct ComponentHelpers; /* Components aren't allowed to have copy constructors, as this would mess up parent hierarchies. You might need to give your subclasses a private dummy constructor to avoid compiler warnings. */ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Component) //============================================================================== #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // This is included here just to cause a compile error if your code is still handling // drag-and-drop with this method. If so, just update it to use the new FileDragAndDropTarget // class, which is easy (just make your class inherit from FileDragAndDropTarget, and // implement its methods instead of this Component method). virtual void filesDropped (const StringArray&, int, int) {} // This is included here to cause an error if you use or overload it - it has been deprecated in // favour of contains (Point) void contains (int, int) JUCE_DELETED_FUNCTION; #endif protected: //============================================================================== /** @internal */ virtual ComponentPeer* createNewPeer (int styleFlags, void* nativeWindowToAttachTo); #endif }; #endif // JUCE_COMPONENT_H_INCLUDED juce_ComponentListener.cpp000066400000000000000000000027231320201440200342160ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ void ComponentListener::componentMovedOrResized (Component&, bool, bool) {} void ComponentListener::componentBroughtToFront (Component&) {} void ComponentListener::componentVisibilityChanged (Component&) {} void ComponentListener::componentChildrenChanged (Component&) {} void ComponentListener::componentParentHierarchyChanged (Component&) {} void ComponentListener::componentNameChanged (Component&) {} void ComponentListener::componentBeingDeleted (Component&) {} libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.h000066400000000000000000000103561320201440200337430ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_COMPONENTLISTENER_H_INCLUDED #define JUCE_COMPONENTLISTENER_H_INCLUDED //============================================================================== /** Gets informed about changes to a component's hierarchy or position. To monitor a component for changes, register a subclass of ComponentListener with the component using Component::addComponentListener(). Be sure to deregister listeners before you delete them! @see Component::addComponentListener, Component::removeComponentListener */ class JUCE_API ComponentListener { public: /** Destructor. */ virtual ~ComponentListener() {} /** Called when the component's position or size changes. @param component the component that was moved or resized @param wasMoved true if the component's top-left corner has just moved @param wasResized true if the component's width or height has just changed @see Component::setBounds, Component::resized, Component::moved */ virtual void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); /** Called when the component is brought to the top of the z-order. @param component the component that was moved @see Component::toFront, Component::broughtToFront */ virtual void componentBroughtToFront (Component& component); /** Called when the component is made visible or invisible. @param component the component that changed @see Component::setVisible */ virtual void componentVisibilityChanged (Component& component); /** Called when the component has children added or removed, or their z-order changes. @param component the component whose children have changed @see Component::childrenChanged, Component::addChildComponent, Component::removeChildComponent */ virtual void componentChildrenChanged (Component& component); /** Called to indicate that the component's parents have changed. When a component is added or removed from its parent, all of its children will produce this notification (recursively - so all children of its children will also be called as well). @param component the component that this listener is registered with @see Component::parentHierarchyChanged */ virtual void componentParentHierarchyChanged (Component& component); /** Called when the component's name is changed. @see Component::setName, Component::getName */ virtual void componentNameChanged (Component& component); /** Called when the component is in the process of being deleted. This callback is made from inside the destructor, so be very, very cautious about what you do in here. In particular, bear in mind that it's the Component base class's destructor that calls this - so if the object that's being deleted is a subclass of Component, then the subclass layers of the object will already have been destructed when it gets to this point! */ virtual void componentBeingDeleted (Component& component); }; #endif // JUCE_COMPONENTLISTENER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.cpp000066400000000000000000000327261320201440200322440ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ Desktop::Desktop() : mouseSources (new MouseInputSource::SourceList()), mouseClickCounter (0), mouseWheelCounter (0), kioskModeComponent (nullptr), kioskModeReentrant (false), allowedOrientations (allOrientations), masterScaleFactor ((float) getDefaultMasterScale()) { displays = new Displays (*this); } Desktop::~Desktop() { setScreenSaverEnabled (true); jassert (instance == this); instance = nullptr; // doh! If you don't delete all your windows before exiting, you're going to // be leaking memory! jassert (desktopComponents.size() == 0); } Desktop& JUCE_CALLTYPE Desktop::getInstance() { if (instance == nullptr) instance = new Desktop(); return *instance; } Desktop* Desktop::instance = nullptr; //============================================================================== int Desktop::getNumComponents() const noexcept { return desktopComponents.size(); } Component* Desktop::getComponent (const int index) const noexcept { return desktopComponents [index]; } Component* Desktop::findComponentAt (Point screenPosition) const { ASSERT_MESSAGE_MANAGER_IS_LOCKED for (int i = desktopComponents.size(); --i >= 0;) { Component* const c = desktopComponents.getUnchecked(i); if (c->isVisible()) { const Point relative (c->getLocalPoint (nullptr, screenPosition)); if (c->contains (relative)) return c->getComponentAt (relative); } } return nullptr; } //============================================================================== LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept { if (currentLookAndFeel == nullptr) { if (defaultLookAndFeel == nullptr) defaultLookAndFeel = new LookAndFeel_V2(); currentLookAndFeel = defaultLookAndFeel; } return *currentLookAndFeel; } void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel) { ASSERT_MESSAGE_MANAGER_IS_LOCKED currentLookAndFeel = newDefaultLookAndFeel; for (int i = getNumComponents(); --i >= 0;) if (Component* const c = getComponent (i)) c->sendLookAndFeelChange(); } //============================================================================== void Desktop::addDesktopComponent (Component* const c) { jassert (c != nullptr); jassert (! desktopComponents.contains (c)); desktopComponents.addIfNotAlreadyThere (c); } void Desktop::removeDesktopComponent (Component* const c) { desktopComponents.removeFirstMatchingValue (c); } void Desktop::componentBroughtToFront (Component* const c) { const int index = desktopComponents.indexOf (c); jassert (index >= 0); if (index >= 0) { int newIndex = -1; if (! c->isAlwaysOnTop()) { newIndex = desktopComponents.size(); while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop()) --newIndex; --newIndex; } desktopComponents.move (index, newIndex); } } //============================================================================== Point Desktop::getMousePosition() { return getMousePositionFloat().roundToInt(); } Point Desktop::getMousePositionFloat() { return getInstance().getMainMouseSource().getScreenPosition(); } void Desktop::setMousePosition (Point newPosition) { getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat()); } Point Desktop::getLastMouseDownPosition() { return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt(); } int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; } int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; } void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; } void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; } const Array& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; } int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); } int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); } MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); } MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); } MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); } void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); } //============================================================================== void Desktop::addFocusChangeListener (FocusChangeListener* const listener) { focusListeners.add (listener); } void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) { focusListeners.remove (listener); } void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); } void Desktop::handleAsyncUpdate() { // The component may be deleted during this operation, but we'll use a SafePointer rather than a // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer). WeakReference currentFocus (Component::getCurrentlyFocusedComponent()); focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus); } //============================================================================== void Desktop::resetTimer() { if (mouseListeners.size() == 0) stopTimer(); else startTimer (100); lastFakeMouseMove = getMousePositionFloat(); } ListenerList& Desktop::getMouseListeners() { resetTimer(); return mouseListeners; } void Desktop::addGlobalMouseListener (MouseListener* const listener) { ASSERT_MESSAGE_MANAGER_IS_LOCKED mouseListeners.add (listener); resetTimer(); } void Desktop::removeGlobalMouseListener (MouseListener* const listener) { ASSERT_MESSAGE_MANAGER_IS_LOCKED mouseListeners.remove (listener); resetTimer(); } void Desktop::timerCallback() { if (lastFakeMouseMove != getMousePositionFloat()) sendMouseMove(); } void Desktop::sendMouseMove() { if (! mouseListeners.isEmpty()) { startTimer (20); lastFakeMouseMove = getMousePositionFloat(); if (Component* const target = findComponentAt (lastFakeMouseMove.roundToInt())) { Component::BailOutChecker checker (target); const Point pos (target->getLocalPoint (nullptr, lastFakeMouseMove)); const Time now (Time::getCurrentTime()); const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(), target, target, now, pos, now, 0, false); if (me.mods.isAnyMouseButtonDown()) mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me); else mouseListeners.callChecked (checker, &MouseListener::mouseMove, me); } } } //============================================================================== Desktop::Displays::Displays (Desktop& desktop) { init (desktop); } Desktop::Displays::~Displays() {} const Desktop::Displays::Display& Desktop::Displays::getMainDisplay() const noexcept { ASSERT_MESSAGE_MANAGER_IS_LOCKED jassert (displays.getReference(0).isMain); return displays.getReference(0); } const Desktop::Displays::Display& Desktop::Displays::getDisplayContaining (Point position) const noexcept { ASSERT_MESSAGE_MANAGER_IS_LOCKED const Display* best = &displays.getReference(0); double bestDistance = 1.0e10; for (int i = displays.size(); --i >= 0;) { const Display& d = displays.getReference(i); if (d.totalArea.contains (position)) { best = &d; break; } const double distance = d.totalArea.getCentre().getDistanceFrom (position); if (distance < bestDistance) { bestDistance = distance; best = &d; } } return *best; } RectangleList Desktop::Displays::getRectangleList (bool userAreasOnly) const { ASSERT_MESSAGE_MANAGER_IS_LOCKED RectangleList rl; for (int i = 0; i < displays.size(); ++i) { const Display& d = displays.getReference(i); rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea); } return rl; } Rectangle Desktop::Displays::getTotalBounds (bool userAreasOnly) const { return getRectangleList (userAreasOnly).getBounds(); } bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept; bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept { return d1.userArea == d2.userArea && d1.totalArea == d2.totalArea && d1.scale == d2.scale && d1.isMain == d2.isMain; } bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept; bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept { return ! (d1 == d2); } void Desktop::Displays::init (Desktop& desktop) { findDisplays (desktop.getGlobalScaleFactor()); } void Desktop::Displays::refresh() { Array oldDisplays; oldDisplays.swapWith (displays); init (Desktop::getInstance()); if (oldDisplays != displays) { for (int i = ComponentPeer::getNumPeers(); --i >= 0;) if (ComponentPeer* const peer = ComponentPeer::getPeer (i)) peer->handleScreenSizeChange(); } } //============================================================================== void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars) { if (kioskModeReentrant) return; const ScopedValueSetter setter (kioskModeReentrant, true, false); if (kioskModeComponent != componentToUse) { // agh! Don't delete or remove a component from the desktop while it's still the kiosk component! jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr); if (Component* const oldKioskComp = kioskModeComponent) { kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one) setKioskComponent (oldKioskComp, false, allowMenusAndBars); oldKioskComp->setBounds (kioskComponentOriginalBounds); } kioskModeComponent = componentToUse; if (kioskModeComponent != nullptr) { // Only components that are already on the desktop can be put into kiosk mode! jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr); kioskComponentOriginalBounds = kioskModeComponent->getBounds(); setKioskComponent (kioskModeComponent, true, allowMenusAndBars); } } } //============================================================================== void Desktop::setOrientationsEnabled (const int newOrientations) { // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation. jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0); allowedOrientations = newOrientations; } bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept { // Make sure you only pass one valid flag in here... jassert (orientation == upright || orientation == upsideDown || orientation == rotatedClockwise || orientation == rotatedAntiClockwise); return (allowedOrientations & orientation) != 0; } void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept { ASSERT_MESSAGE_MANAGER_IS_LOCKED if (masterScaleFactor != newScaleFactor) { masterScaleFactor = newScaleFactor; displays->refresh(); } } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.h000066400000000000000000000461531320201440200317100ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DESKTOP_H_INCLUDED #define JUCE_DESKTOP_H_INCLUDED //============================================================================== /** Classes can implement this interface and register themselves with the Desktop class to receive callbacks when the currently focused component changes. @see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener */ class JUCE_API FocusChangeListener { public: /** Destructor. */ virtual ~FocusChangeListener() {} /** Callback to indicate that the currently focused component has changed. */ virtual void globalFocusChanged (Component* focusedComponent) = 0; }; //============================================================================== /** Describes and controls aspects of the computer's desktop. */ class JUCE_API Desktop : private DeletedAtShutdown, private Timer, private AsyncUpdater { public: //============================================================================== /** There's only one desktop object, and this method will return it. */ static Desktop& JUCE_CALLTYPE getInstance(); //============================================================================== /** Returns the mouse position. The coordinates are relative to the top-left of the main monitor. Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and you should only resort to grabbing the global mouse position if there's really no way to get the coordinates via a mouse event callback instead. */ static Point getMousePosition(); /** Makes the mouse pointer jump to a given location. The coordinates are relative to the top-left of the main monitor. */ static void setMousePosition (Point newPosition); /** Returns the last position at which a mouse button was pressed. Note that this is just a shortcut for calling getMainMouseSource().getLastMouseDownPosition(), and in a multi-touch environment, it doesn't make much sense. ALWAYS prefer to get this information via other means, such as MouseEvent::getMouseDownScreenPosition() if possible, and only ever call this as a last resort. */ static Point getLastMouseDownPosition(); /** Returns the number of times the mouse button has been clicked since the app started. Each mouse-down event increments this number by 1. @see getMouseWheelMoveCounter */ int getMouseButtonClickCounter() const noexcept; /** Returns the number of times the mouse wheel has been moved since the app started. Each mouse-wheel event increments this number by 1. @see getMouseButtonClickCounter */ int getMouseWheelMoveCounter() const noexcept; //============================================================================== /** This lets you prevent the screensaver from becoming active. Handy if you're running some sort of presentation app where having a screensaver appear would be annoying. Pass false to disable the screensaver, and true to re-enable it. (Note that this won't enable a screensaver unless the user has actually set one up). The disablement will only happen while the Juce application is the foreground process - if another task is running in front of it, then the screensaver will be unaffected. @see isScreenSaverEnabled */ static void setScreenSaverEnabled (bool isEnabled); /** Returns true if the screensaver has not been turned off. This will return the last value passed into setScreenSaverEnabled(). Note that it won't tell you whether the user is actually using a screen saver, just whether this app is deliberately preventing one from running. @see setScreenSaverEnabled */ static bool isScreenSaverEnabled(); //============================================================================== /** Registers a MouseListener that will receive all mouse events that occur on any component. @see removeGlobalMouseListener */ void addGlobalMouseListener (MouseListener* listener); /** Unregisters a MouseListener that was added with the addGlobalMouseListener() method. @see addGlobalMouseListener */ void removeGlobalMouseListener (MouseListener* listener); //============================================================================== /** Registers a MouseListener that will receive a callback whenever the focused component changes. */ void addFocusChangeListener (FocusChangeListener* listener); /** Unregisters a listener that was added with addFocusChangeListener(). */ void removeFocusChangeListener (FocusChangeListener* listener); //============================================================================== /** Takes a component and makes it full-screen, removing the taskbar, dock, etc. The component must already be on the desktop for this method to work. It will be resized to completely fill the screen and any extraneous taskbars, menu bars, etc will be hidden. To exit kiosk mode, just call setKioskModeComponent (nullptr). When this is called, the component that's currently being used will be resized back to the size and position it was in before being put into this mode. If allowMenusAndBars is true, things like the menu and dock (on mac) are still allowed to pop up when the mouse moves onto them. If this is false, it'll try to hide as much on-screen paraphenalia as possible. */ void setKioskModeComponent (Component* componentToUse, bool allowMenusAndBars = true); /** Returns the component that is currently being used in kiosk-mode. This is the component that was last set by setKioskModeComponent(). If none has been set, this returns nullptr. */ Component* getKioskModeComponent() const noexcept { return kioskModeComponent; } //============================================================================== /** Returns the number of components that are currently active as top-level desktop windows. @see getComponent, Component::addToDesktop */ int getNumComponents() const noexcept; /** Returns one of the top-level desktop window components. The index is from 0 to getNumComponents() - 1. This could return 0 if the index is out-of-range. @see getNumComponents, Component::addToDesktop */ Component* getComponent (int index) const noexcept; /** Finds the component at a given screen location. This will drill down into top-level windows to find the child component at the given position. Returns nullptr if the coordinates are inside a non-Juce window. */ Component* findComponentAt (Point screenPosition) const; /** The Desktop object has a ComponentAnimator instance which can be used for performing your animations. Having a single shared ComponentAnimator object makes it more efficient when multiple components are being moved around simultaneously. It's also more convenient than having to manage your own instance of one. @see ComponentAnimator */ ComponentAnimator& getAnimator() noexcept { return animator; } //============================================================================== /** Returns the current default look-and-feel for components which don't have one explicitly set. @see setDefaultLookAndFeel */ LookAndFeel& getDefaultLookAndFeel() noexcept; /** Changes the default look-and-feel. @param newDefaultLookAndFeel the new look-and-feel object to use - if this is set to nullptr, it will revert to using the system's default one. The object passed-in must be deleted by the caller when it's no longer needed. @see getDefaultLookAndFeel */ void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel); //============================================================================== /** Provides access to the array of mouse sources, for iteration. In a traditional single-mouse system, there might be only one MouseInputSource. On a multi-touch system, there could be one input source per potential finger. The number of mouse sources returned here may increase dynamically as the program runs. To find out how many mouse events are currently happening, use getNumDraggingMouseSources(). */ const Array& getMouseSources() const noexcept; /** Returns the number of MouseInputSource objects the system has at its disposal. In a traditional single-mouse system, there might be only one MouseInputSource. On a multi-touch system, there could be one input source per potential finger. The number of mouse sources returned here may increase dynamically as the program runs. To find out how many mouse events are currently happening, use getNumDraggingMouseSources(). @see getMouseSource */ int getNumMouseSources() const noexcept; /** Returns one of the system's MouseInputSource objects. The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return a null pointer. In a traditional single-mouse system, there might be only one object. On a multi-touch system, there could be one input source per potential finger. */ MouseInputSource* getMouseSource (int index) const noexcept; /** Returns the main mouse input device that the system is using. @see getNumMouseSources() */ MouseInputSource getMainMouseSource() const noexcept; /** Returns the number of mouse-sources that are currently being dragged. In a traditional single-mouse system, this will be 0 or 1, depending on whether a juce component has the button down on it. In a multi-touch system, this could be any number from 0 to the number of simultaneous touches that can be detected. */ int getNumDraggingMouseSources() const noexcept; /** Returns one of the mouse sources that's currently being dragged. The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is out of range, or if no mice or fingers are down, this will return a null pointer. */ MouseInputSource* getDraggingMouseSource (int index) const noexcept; /** Ensures that a non-stop stream of mouse-drag events will be sent during the current mouse-drag operation. This allows you to make sure that mouseDrag() events are sent continuously, even when the mouse isn't moving. This can be useful for things like auto-scrolling components when the mouse is near an edge. Call this method during a mouseDown() or mouseDrag() callback, specifying the minimum interval between consecutive mouse drag callbacks. The callbacks will continue until the mouse is released, and then the interval will be reset, so you need to make sure it's called every time you begin a drag event. Passing an interval of 0 or less will cancel the auto-repeat. @see mouseDrag */ void beginDragAutoRepeat (int millisecondsBetweenCallbacks); //============================================================================== /** In a tablet device which can be turned around, this is used to inidicate the orientation. */ enum DisplayOrientation { upright = 1, /**< Indicates that the display is the normal way up. */ upsideDown = 2, /**< Indicates that the display is upside-down. */ rotatedClockwise = 4, /**< Indicates that the display is turned 90 degrees clockwise from its upright position. */ rotatedAntiClockwise = 8, /**< Indicates that the display is turned 90 degrees anti-clockwise from its upright position. */ allOrientations = 1 + 2 + 4 + 8 /**< A combination of all the orientation values */ }; /** In a tablet device which can be turned around, this returns the current orientation. */ DisplayOrientation getCurrentOrientation() const; /** Sets which orientations the display is allowed to auto-rotate to. For devices that support rotating desktops, this lets you specify which of the orientations your app can use. The parameter is a bitwise or-ed combination of the values in DisplayOrientation, and must contain at least one set bit. */ void setOrientationsEnabled (int allowedOrientations); /** Returns whether the display is allowed to auto-rotate to the given orientation. Each orientation can be enabled using setOrientationEnabled(). By default, all orientations are allowed. */ bool isOrientationEnabled (DisplayOrientation orientation) const noexcept; //============================================================================== class JUCE_API Displays { public: /** Contains details about a display device. */ struct Display { /** This is the bounds of the area of this display which isn't covered by OS-dependent objects like the taskbar, menu bar, etc. */ Rectangle userArea; /** This is the total physical area of this display, including any taskbars, etc */ Rectangle totalArea; /** This is the scale-factor of this display. If you create a component with size 1x1, this scale factor indicates the actual size of the component in terms of physical pixels. For higher-resolution displays, it may be a value greater than 1.0 */ double scale; /** The DPI of the display. This is the number of physical pixels per inch. To get the number of logical pixels per inch, divide this by the Display::scale value. */ double dpi; /** This will be true if this is the user's main screen. */ bool isMain; }; /** Returns the display which acts as user's main screen. */ const Display& getMainDisplay() const noexcept; /** Returns the display which contains a particular point. If the point lies outside all the displays, the nearest one will be returned. */ const Display& getDisplayContaining (Point position) const noexcept; /** Returns a RectangleList made up of all the displays. */ RectangleList getRectangleList (bool userAreasOnly) const; /** Returns the smallest bounding box which contains all the displays. */ Rectangle getTotalBounds (bool userAreasOnly) const; /** The list of displays. */ Array displays; #ifndef DOXYGEN /** @internal */ void refresh(); #endif private: friend class Desktop; friend struct ContainerDeletePolicy; Displays (Desktop&); ~Displays(); void init (Desktop&); void findDisplays (float masterScale); }; const Displays& getDisplays() const noexcept { return *displays; } //============================================================================== /** Sets a global scale factor to be used for all desktop windows. Setting this will also scale the monitor sizes that are returned by getDisplays(). */ void setGlobalScaleFactor (float newScaleFactor) noexcept; /** Returns the current global scale factor, as set by setGlobalScaleFactor(). @see setGlobalScaleFactor */ float getGlobalScaleFactor() const noexcept { return masterScaleFactor; } //============================================================================== /** True if the OS supports semitransparent windows */ static bool canUseSemiTransparentWindows() noexcept; private: //============================================================================== static Desktop* instance; friend class Component; friend class ComponentPeer; friend class MouseInputSourceInternal; friend class DeletedAtShutdown; friend class TopLevelWindowManager; ScopedPointer mouseSources; ListenerList mouseListeners; ListenerList focusListeners; Array desktopComponents; Array peers; ScopedPointer displays; Point lastFakeMouseMove; void sendMouseMove(); int mouseClickCounter, mouseWheelCounter; void incrementMouseClickCounter() noexcept; void incrementMouseWheelCounter() noexcept; ScopedPointer defaultLookAndFeel; WeakReference currentLookAndFeel; Component* kioskModeComponent; Rectangle kioskComponentOriginalBounds; bool kioskModeReentrant; int allowedOrientations; float masterScaleFactor; ComponentAnimator animator; void timerCallback() override; void resetTimer(); ListenerList& getMouseListeners(); void addDesktopComponent (Component*); void removeDesktopComponent (Component*); void componentBroughtToFront (Component*); void setKioskComponent (Component*, bool shouldBeEnabled, bool allowMenusAndBars); void triggerFocusCallback(); void handleAsyncUpdate() override; static Point getMousePositionFloat(); static double getDefaultMasterScale(); Desktop(); ~Desktop(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop) }; #endif // JUCE_DESKTOP_H_INCLUDED juce_ModalComponentManager.cpp000066400000000000000000000171531320201440200347630ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class ModalComponentManager::ModalItem : public ComponentMovementWatcher { public: ModalItem (Component* const comp, const bool autoDelete_) : ComponentMovementWatcher (comp), component (comp), returnValue (0), isActive (true), autoDelete (autoDelete_) { jassert (comp != nullptr); } void componentMovedOrResized (bool, bool) override {} void componentPeerChanged() override { if (! component->isShowing()) cancel(); } void componentVisibilityChanged() override { if (! component->isShowing()) cancel(); } void componentBeingDeleted (Component& comp) override { ComponentMovementWatcher::componentBeingDeleted (comp); if (component == &comp || comp.isParentOf (component)) { autoDelete = false; cancel(); } } void cancel() { if (isActive) { isActive = false; if (ModalComponentManager* mcm = ModalComponentManager::getInstanceWithoutCreating()) mcm->triggerAsyncUpdate(); } } Component* component; OwnedArray callbacks; int returnValue; bool isActive, autoDelete; private: JUCE_DECLARE_NON_COPYABLE (ModalItem) }; //============================================================================== ModalComponentManager::ModalComponentManager() { } ModalComponentManager::~ModalComponentManager() { stack.clear(); clearSingletonInstance(); } juce_ImplementSingleton_SingleThreaded (ModalComponentManager) //============================================================================== void ModalComponentManager::startModal (Component* component, bool autoDelete) { if (component != nullptr) stack.add (new ModalItem (component, autoDelete)); } void ModalComponentManager::attachCallback (Component* component, Callback* callback) { if (callback != nullptr) { ScopedPointer callbackDeleter (callback); for (int i = stack.size(); --i >= 0;) { ModalItem* const item = stack.getUnchecked(i); if (item->component == component) { item->callbacks.add (callback); callbackDeleter.release(); break; } } } } void ModalComponentManager::endModal (Component* component) { for (int i = stack.size(); --i >= 0;) { ModalItem* const item = stack.getUnchecked(i); if (item->component == component) item->cancel(); } } void ModalComponentManager::endModal (Component* component, int returnValue) { for (int i = stack.size(); --i >= 0;) { ModalItem* const item = stack.getUnchecked(i); if (item->component == component) { item->returnValue = returnValue; item->cancel(); } } } int ModalComponentManager::getNumModalComponents() const { int n = 0; for (int i = 0; i < stack.size(); ++i) if (stack.getUnchecked(i)->isActive) ++n; return n; } Component* ModalComponentManager::getModalComponent (const int index) const { int n = 0; for (int i = stack.size(); --i >= 0;) { const ModalItem* const item = stack.getUnchecked(i); if (item->isActive) if (n++ == index) return item->component; } return nullptr; } bool ModalComponentManager::isModal (Component* const comp) const { for (int i = stack.size(); --i >= 0;) { const ModalItem* const item = stack.getUnchecked(i); if (item->isActive && item->component == comp) return true; } return false; } bool ModalComponentManager::isFrontModalComponent (Component* const comp) const { return comp == getModalComponent (0); } void ModalComponentManager::handleAsyncUpdate() { for (int i = stack.size(); --i >= 0;) { const ModalItem* const item = stack.getUnchecked(i); if (! item->isActive) { ScopedPointer deleter (stack.removeAndReturn (i)); Component::SafePointer compToDelete (item->autoDelete ? item->component : nullptr); for (int j = item->callbacks.size(); --j >= 0;) item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue); compToDelete.deleteAndZero(); } } } void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus) { ComponentPeer* lastOne = nullptr; for (int i = 0; i < getNumModalComponents(); ++i) { Component* const c = getModalComponent (i); if (c == nullptr) break; ComponentPeer* peer = c->getPeer(); if (peer != nullptr && peer != lastOne) { if (lastOne == nullptr) { peer->toFront (topOneShouldGrabFocus); if (topOneShouldGrabFocus) peer->grabFocus(); } else peer->toBehind (lastOne); lastOne = peer; } } } bool ModalComponentManager::cancelAllModalComponents() { const int numModal = getNumModalComponents(); for (int i = numModal; --i >= 0;) if (Component* const c = getModalComponent(i)) c->exitModalState (0); return numModal > 0; } #if JUCE_MODAL_LOOPS_PERMITTED class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback { public: ReturnValueRetriever (int& v, bool& done) : value (v), finished (done) {} void modalStateFinished (int returnValue) { finished = true; value = returnValue; } private: int& value; bool& finished; JUCE_DECLARE_NON_COPYABLE (ReturnValueRetriever) }; int ModalComponentManager::runEventLoopForCurrentComponent() { // This can only be run from the message thread! jassert (MessageManager::getInstance()->isThisTheMessageThread()); int returnValue = 0; if (Component* currentlyModal = getModalComponent (0)) { FocusRestorer focusRestorer; bool finished = false; attachCallback (currentlyModal, new ReturnValueRetriever (returnValue, finished)); JUCE_TRY { while (! finished) { if (! MessageManager::getInstance()->runDispatchLoopUntil (20)) break; } } JUCE_CATCH_EXCEPTION } return returnValue; } #endif juce_ModalComponentManager.h000066400000000000000000000354151320201440200344310ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/components/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_MODALCOMPONENTMANAGER_H_INCLUDED #define JUCE_MODALCOMPONENTMANAGER_H_INCLUDED //============================================================================== /** Manages the system's stack of modal components. Normally you'll just use the Component methods to invoke modal states in components, and won't have to deal with this class directly, but this is the singleton object that's used internally to manage the stack. @see Component::enterModalState, Component::exitModalState, Component::isCurrentlyModal, Component::getCurrentlyModalComponent, Component::isCurrentlyBlockedByAnotherModalComponent */ class JUCE_API ModalComponentManager : private AsyncUpdater, private DeletedAtShutdown { public: //============================================================================== /** Receives callbacks when a modal component is dismissed. You can register a callback using Component::enterModalState() or ModalComponentManager::attachCallback(). For some quick ways of creating callback objects, see the ModalCallbackFunction class. @see ModalCallbackFunction */ class Callback { public: /** */ Callback() {} /** Destructor. */ virtual ~Callback() {} /** Called to indicate that a modal component has been dismissed. You can register a callback using Component::enterModalState() or ModalComponentManager::attachCallback(). The returnValue parameter is the value that was passed to Component::exitModalState() when the component was dismissed. The callback object will be deleted shortly after this method is called. */ virtual void modalStateFinished (int returnValue) = 0; }; //============================================================================== juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager) //============================================================================== /** Returns the number of components currently being shown modally. @see getModalComponent */ int getNumModalComponents() const; /** Returns one of the components being shown modally. An index of 0 is the most recently-shown, topmost component. */ Component* getModalComponent (int index) const; /** Returns true if the specified component is in a modal state. */ bool isModal (Component* component) const; /** Returns true if the specified component is currently the topmost modal component. */ bool isFrontModalComponent (Component* component) const; /** Adds a new callback that will be called when the specified modal component is dismissed. If the component is modal, then when it is dismissed, either by being hidden, or by calling Component::exitModalState(), then the Callback::modalStateFinished() method will be called. Each component can have any number of callbacks associated with it, and this one is added to that list. The object that is passed in will be deleted by the manager when it's no longer needed. If the given component is not currently modal, the callback object is deleted immediately and no action is taken. */ void attachCallback (Component* component, Callback* callback); /** Brings any modal components to the front. */ void bringModalComponentsToFront (bool topOneShouldGrabFocus = true); /** Calls exitModalState (0) on any components that are currently modal. @returns true if any components were modal; false if nothing needed cancelling */ bool cancelAllModalComponents(); #if JUCE_MODAL_LOOPS_PERMITTED /** Runs the event loop until the currently topmost modal component is dismissed, and returns the exit code for that component. */ int runEventLoopForCurrentComponent(); #endif protected: /** Creates a ModalComponentManager. You shouldn't ever call the constructor - it's a singleton, so use ModalComponentManager::getInstance() */ ModalComponentManager(); /** Destructor. */ ~ModalComponentManager(); /** @internal */ void handleAsyncUpdate() override; private: //============================================================================== class ModalItem; class ReturnValueRetriever; friend class Component; friend struct ContainerDeletePolicy; OwnedArray stack; void startModal (Component*, bool autoDelete); void endModal (Component*, int returnValue); void endModal (Component*); JUCE_DECLARE_NON_COPYABLE (ModalComponentManager) }; //============================================================================== /** This class provides some handy utility methods for creating ModalComponentManager::Callback objects that will invoke a static function with some parameters when a modal component is dismissed. */ class ModalCallbackFunction { public: //============================================================================== /** This is a utility function to create a ModalComponentManager::Callback that will call a static function with a parameter. The function that you supply must take two parameters - the first being an int, which is the result code that was used when the modal component was dismissed, and the second can be a custom type. Note that this custom value will be copied and stored, so it must be a primitive type or a class that provides copy-by-value semantics. E.g. @code static void myCallbackFunction (int modalResult, double customValue) { if (modalResult == 1) doSomethingWith (customValue); } Component* someKindOfComp; ... someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0)); @endcode @see ModalComponentManager::Callback */ template static ModalComponentManager::Callback* create (void (*functionToCall) (int, ParamType), ParamType parameterValue) { return new FunctionCaller1 (functionToCall, parameterValue); } //============================================================================== /** This is a utility function to create a ModalComponentManager::Callback that will call a static function with two custom parameters. The function that you supply must take three parameters - the first being an int, which is the result code that was used when the modal component was dismissed, and the next two are your custom types. Note that these custom values will be copied and stored, so they must be primitive types or classes that provide copy-by-value semantics. E.g. @code static void myCallbackFunction (int modalResult, double customValue1, String customValue2) { if (modalResult == 1) doSomethingWith (customValue1, customValue2); } Component* someKindOfComp; ... someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0, String ("xyz"))); @endcode @see ModalComponentManager::Callback */ template static ModalComponentManager::Callback* withParam (void (*functionToCall) (int, ParamType1, ParamType2), ParamType1 parameterValue1, ParamType2 parameterValue2) { return new FunctionCaller2 (functionToCall, parameterValue1, parameterValue2); } //============================================================================== /** This is a utility function to create a ModalComponentManager::Callback that will call a static function with a component. The function that you supply must take two parameters - the first being an int, which is the result code that was used when the modal component was dismissed, and the second can be a Component class. The component will be stored as a WeakReference, so that if it gets deleted before this callback is invoked, the pointer that is passed to the function will be null. E.g. @code static void myCallbackFunction (int modalResult, Slider* mySlider) { if (modalResult == 1 && mySlider != nullptr) // (must check that mySlider isn't null in case it was deleted..) mySlider->setValue (0.0); } Component* someKindOfComp; Slider* mySlider; ... someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider)); @endcode @see ModalComponentManager::Callback */ template static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*), ComponentType* component) { return new ComponentCaller1 (functionToCall, component); } //============================================================================== /** Creates a ModalComponentManager::Callback that will call a static function with a component. The function that you supply must take three parameters - the first being an int, which is the result code that was used when the modal component was dismissed, the second being a Component class, and the third being a custom type (which must be a primitive type or have copy-by-value semantics). The component will be stored as a WeakReference, so that if it gets deleted before this callback is invoked, the pointer that is passed into the function will be null. E.g. @code static void myCallbackFunction (int modalResult, Slider* mySlider, String customParam) { if (modalResult == 1 && mySlider != nullptr) // (must check that mySlider isn't null in case it was deleted..) mySlider->setName (customParam); } Component* someKindOfComp; Slider* mySlider; ... someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider, String ("hello"))); @endcode @see ModalComponentManager::Callback */ template static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*, ParamType), ComponentType* component, ParamType param) { return new ComponentCaller2 (functionToCall, component, param); } private: //============================================================================== template class FunctionCaller1 : public ModalComponentManager::Callback { public: typedef void (*FunctionType) (int, ParamType); FunctionCaller1 (FunctionType& f, ParamType& p1) : function (f), param (p1) {} void modalStateFinished (int returnValue) { function (returnValue, param); } private: const FunctionType function; ParamType param; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller1) }; template class FunctionCaller2 : public ModalComponentManager::Callback { public: typedef void (*FunctionType) (int, ParamType1, ParamType2); FunctionCaller2 (FunctionType& f, ParamType1& p1, ParamType2& p2) : function (f), param1 (p1), param2 (p2) {} void modalStateFinished (int returnValue) { function (returnValue, param1, param2); } private: const FunctionType function; ParamType1 param1; ParamType2 param2; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller2) }; template class ComponentCaller1 : public ModalComponentManager::Callback { public: typedef void (*FunctionType) (int, ComponentType*); ComponentCaller1 (FunctionType& f, ComponentType* c) : function (f), comp (c) {} void modalStateFinished (int returnValue) { function (returnValue, static_cast (comp.get())); } private: const FunctionType function; WeakReference comp; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller1) }; template class ComponentCaller2 : public ModalComponentManager::Callback { public: typedef void (*FunctionType) (int, ComponentType*, ParamType1); ComponentCaller2 (FunctionType& f, ComponentType* c, ParamType1 p1) : function (f), comp (c), param1 (p1) {} void modalStateFinished (int returnValue) { function (returnValue, static_cast (comp.get()), param1); } private: const FunctionType function; WeakReference comp; ParamType1 param1; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller2) }; ModalCallbackFunction(); ~ModalCallbackFunction(); JUCE_DECLARE_NON_COPYABLE (ModalCallbackFunction) }; #endif // JUCE_MODALCOMPONENTMANAGER_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/000077500000000000000000000000001320201440200266665ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.cpp000066400000000000000000000175021320201440200321260ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ Drawable::Drawable() { setInterceptsMouseClicks (false, false); setPaintingIsUnclipped (true); } Drawable::Drawable (const Drawable& other) : Component (other.getName()) { setComponentID (other.getComponentID()); } Drawable::~Drawable() { } //============================================================================== void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const { const_cast (this)->nonConstDraw (g, opacity, transform); } void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform) { Graphics::ScopedSaveState ss (g); g.addTransform (AffineTransform::translation ((float) -(originRelativeToComponent.x), (float) -(originRelativeToComponent.y)) .followedBy (getTransform()) .followedBy (transform)); if (! g.isClipEmpty()) { if (opacity < 1.0f) { g.beginTransparencyLayer (opacity); paintEntireComponent (g, true); g.endTransparencyLayer(); } else { paintEntireComponent (g, true); } } } void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const { draw (g, opacity, AffineTransform::translation (x, y)); } void Drawable::drawWithin (Graphics& g, const Rectangle& destArea, RectanglePlacement placement, float opacity) const { draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea)); } //============================================================================== DrawableComposite* Drawable::getParent() const { return dynamic_cast (getParentComponent()); } void Drawable::transformContextToCorrectOrigin (Graphics& g) { g.setOrigin (originRelativeToComponent); } void Drawable::parentHierarchyChanged() { setBoundsToEnclose (getDrawableBounds()); } void Drawable::setBoundsToEnclose (const Rectangle& area) { Drawable* const parent = getParent(); Point parentOrigin; if (parent != nullptr) parentOrigin = parent->originRelativeToComponent; const Rectangle newBounds (area.getSmallestIntegerContainer() + parentOrigin); originRelativeToComponent = parentOrigin - newBounds.getPosition(); setBounds (newBounds); } //============================================================================== bool Drawable::replaceColour (Colour original, Colour replacement) { bool changed = false; for (int i = getNumChildComponents(); --i >= 0;) if (Drawable* d = dynamic_cast (getChildComponent(i))) changed = d->replaceColour (original, replacement) || changed; return changed; } //============================================================================== void Drawable::setOriginWithOriginalSize (Point originWithinParent) { setTransform (AffineTransform::translation (originWithinParent.x, originWithinParent.y)); } void Drawable::setTransformToFit (const Rectangle& area, RectanglePlacement placement) { if (! area.isEmpty()) setTransform (placement.getTransformToFit (getDrawableBounds(), area)); } //============================================================================== Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes) { Drawable* result = nullptr; Image image (ImageFileFormat::loadFrom (data, numBytes)); if (image.isValid()) { DrawableImage* const di = new DrawableImage(); di->setImage (image); result = di; } else { const String asString (String::createStringFromData (data, (int) numBytes)); XmlDocument doc (asString); ScopedPointer outer (doc.getDocumentElement (true)); if (outer != nullptr && outer->hasTagName ("svg")) { ScopedPointer svg (doc.getDocumentElement()); if (svg != nullptr) result = Drawable::createFromSVG (*svg); } } return result; } Drawable* Drawable::createFromImageDataStream (InputStream& dataSource) { MemoryOutputStream mo; mo << dataSource; return createFromImageData (mo.getData(), mo.getDataSize()); } Drawable* Drawable::createFromImageFile (const File& file) { FileInputStream fin (file); return fin.openedOk() ? createFromImageDataStream (fin) : nullptr; } //============================================================================== template class DrawableTypeHandler : public ComponentBuilder::TypeHandler { public: DrawableTypeHandler() : ComponentBuilder::TypeHandler (DrawableClass::valueTreeType) { } Component* addNewComponentFromState (const ValueTree& state, Component* parent) { DrawableClass* const d = new DrawableClass(); if (parent != nullptr) parent->addAndMakeVisible (d); updateComponentFromState (d, state); return d; } void updateComponentFromState (Component* component, const ValueTree& state) { DrawableClass* const d = dynamic_cast (component); jassert (d != nullptr); d->refreshFromValueTree (state, *this->getBuilder()); } }; void Drawable::registerDrawableTypeHandlers (ComponentBuilder& builder) { builder.registerTypeHandler (new DrawableTypeHandler ()); builder.registerTypeHandler (new DrawableTypeHandler ()); builder.registerTypeHandler (new DrawableTypeHandler ()); builder.registerTypeHandler (new DrawableTypeHandler ()); builder.registerTypeHandler (new DrawableTypeHandler ()); } Drawable* Drawable::createFromValueTree (const ValueTree& tree, ComponentBuilder::ImageProvider* imageProvider) { ComponentBuilder builder (tree); builder.setImageProvider (imageProvider); registerDrawableTypeHandlers (builder); ScopedPointer comp (builder.createComponent()); Drawable* const d = dynamic_cast (static_cast (comp)); if (d != nullptr) comp.release(); return d; } //============================================================================== Drawable::ValueTreeWrapperBase::ValueTreeWrapperBase (const ValueTree& state_) : state (state_) { } String Drawable::ValueTreeWrapperBase::getID() const { return state [ComponentBuilder::idProperty]; } void Drawable::ValueTreeWrapperBase::setID (const String& newID) { if (newID.isEmpty()) state.removeProperty (ComponentBuilder::idProperty, nullptr); else state.setProperty (ComponentBuilder::idProperty, newID, nullptr); } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h000066400000000000000000000241631320201440200315740ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLE_H_INCLUDED #define JUCE_DRAWABLE_H_INCLUDED //============================================================================== /** The base class for objects which can draw themselves, e.g. polygons, images, etc. @see DrawableComposite, DrawableImage, DrawablePath, DrawableText */ class JUCE_API Drawable : public Component { protected: //============================================================================== /** The base class can't be instantiated directly. @see DrawableComposite, DrawableImage, DrawablePath, DrawableText */ Drawable(); public: /** Destructor. */ virtual ~Drawable(); //============================================================================== /** Creates a deep copy of this Drawable object. Use this to create a new copy of this and any sub-objects in the tree. */ virtual Drawable* createCopy() const = 0; //============================================================================== /** Renders this Drawable object. Note that the preferred way to render a drawable in future is by using it as a component and adding it to a parent, so you might want to consider that before using this method. @see drawWithin */ void draw (Graphics& g, float opacity, const AffineTransform& transform = AffineTransform::identity) const; /** Renders the Drawable at a given offset within the Graphics context. The coordinates passed-in are used to translate the object relative to its own origin before drawing it - this is basically a quick way of saying: @code draw (g, AffineTransform::translation (x, y)). @endcode Note that the preferred way to render a drawable in future is by using it as a component and adding it to a parent, so you might want to consider that before using this method. */ void drawAt (Graphics& g, float x, float y, float opacity) const; /** Renders the Drawable within a rectangle, scaling it to fit neatly inside without changing its aspect-ratio. The object can placed arbitrarily within the rectangle based on a Justification type, and can either be made as big as possible, or just reduced to fit. Note that the preferred way to render a drawable in future is by using it as a component and adding it to a parent, so you might want to consider that before using this method. @param g the graphics context to render onto @param destArea the target rectangle to fit the drawable into @param placement defines the alignment and rescaling to use to fit this object within the target rectangle. @param opacity the opacity to use, in the range 0 to 1.0 */ void drawWithin (Graphics& g, const Rectangle& destArea, RectanglePlacement placement, float opacity) const; //============================================================================== /** Resets any transformations on this drawable, and positions its origin within its parent component. */ void setOriginWithOriginalSize (Point originWithinParent); /** Sets a transform for this drawable that will position it within the specified area of its parent component. */ void setTransformToFit (const Rectangle& areaInParent, RectanglePlacement placement); /** Returns the DrawableComposite that contains this object, if there is one. */ DrawableComposite* getParent() const; //============================================================================== /** Tries to turn some kind of image file into a drawable. The data could be an image that the ImageFileFormat class understands, or it could be SVG. */ static Drawable* createFromImageData (const void* data, size_t numBytes); /** Tries to turn a stream containing some kind of image data into a drawable. The data could be an image that the ImageFileFormat class understands, or it could be SVG. */ static Drawable* createFromImageDataStream (InputStream& dataSource); /** Tries to turn a file containing some kind of image data into a drawable. The data could be an image that the ImageFileFormat class understands, or it could be SVG. */ static Drawable* createFromImageFile (const File& file); /** Attempts to parse an SVG (Scalable Vector Graphics) document, and to turn this into a Drawable tree. The object returned must be deleted by the caller. If something goes wrong while parsing, it may return nullptr. SVG is a pretty large and complex spec, and this doesn't aim to be a full implementation, but it can return the basic vector objects. */ static Drawable* createFromSVG (const XmlElement& svgDocument); /** Parses an SVG path string and returns it. */ static Path parseSVGPath (const String& svgPath); //============================================================================== /** Tries to create a Drawable from a previously-saved ValueTree. The ValueTree must have been created by the createValueTree() method. If there are any images used within the drawable, you'll need to provide a valid ImageProvider object that can be used to retrieve these images from whatever type of identifier is used to represent them. Internally, this uses a ComponentBuilder, and registerDrawableTypeHandlers(). */ static Drawable* createFromValueTree (const ValueTree& tree, ComponentBuilder::ImageProvider* imageProvider); /** Creates a ValueTree to represent this Drawable. The ValueTree that is returned can be turned back into a Drawable with createFromValueTree(). If there are any images used in this drawable, you'll need to provide a valid ImageProvider object that can be used to create storable representations of them. */ virtual ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const = 0; /** Returns the area that this drawble covers. The result is expressed in this drawable's own coordinate space, and does not take into account any transforms that may be applied to the component. */ virtual Rectangle getDrawableBounds() const = 0; /** Recursively replaces a colour that might be used for filling or stroking. return true if any instances of this colour were found. */ virtual bool replaceColour (Colour originalColour, Colour replacementColour); //============================================================================== /** Internal class used to manage ValueTrees that represent Drawables. */ class ValueTreeWrapperBase { public: ValueTreeWrapperBase (const ValueTree& state); ValueTree& getState() noexcept { return state; } String getID() const; void setID (const String& newID); ValueTree state; }; //============================================================================== /** Registers a set of ComponentBuilder::TypeHandler objects that can be used to load all the different Drawable types from a saved state. @see ComponentBuilder::registerTypeHandler() */ static void registerDrawableTypeHandlers (ComponentBuilder& componentBuilder); protected: //============================================================================== friend class DrawableComposite; friend class DrawableShape; /** @internal */ void transformContextToCorrectOrigin (Graphics&); /** @internal */ void parentHierarchyChanged() override; /** @internal */ void setBoundsToEnclose (const Rectangle&); Point originRelativeToComponent; #ifndef DOXYGEN /** Internal utility class used by Drawables. */ template class Positioner : public RelativeCoordinatePositionerBase { public: Positioner (DrawableType& c) : RelativeCoordinatePositionerBase (c), owner (c) {} bool registerCoordinates() override { return owner.registerCoordinates (*this); } void applyToComponentBounds() override { ComponentScope scope (getComponent()); owner.recalculateCoordinates (&scope); } void applyNewBounds (const Rectangle&) override { jassertfalse; // drawables can't be resized directly! } private: DrawableType& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner) }; Drawable (const Drawable&); #endif private: void nonConstDraw (Graphics&, float opacity, const AffineTransform&); Drawable& operator= (const Drawable&); JUCE_LEAK_DETECTOR (Drawable) }; #endif // JUCE_DRAWABLE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp000066400000000000000000000312141320201440200340050ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DrawableComposite::DrawableComposite() : bounds (Point(), Point (100.0f, 0.0f), Point (0.0f, 100.0f)), updateBoundsReentrant (false) { setContentArea (RelativeRectangle (RelativeCoordinate (0.0), RelativeCoordinate (100.0), RelativeCoordinate (0.0), RelativeCoordinate (100.0))); } DrawableComposite::DrawableComposite (const DrawableComposite& other) : Drawable (other), bounds (other.bounds), markersX (other.markersX), markersY (other.markersY), updateBoundsReentrant (false) { for (int i = 0; i < other.getNumChildComponents(); ++i) if (const Drawable* const d = dynamic_cast (other.getChildComponent(i))) addAndMakeVisible (d->createCopy()); } DrawableComposite::~DrawableComposite() { deleteAllChildren(); } Drawable* DrawableComposite::createCopy() const { return new DrawableComposite (*this); } //============================================================================== Rectangle DrawableComposite::getDrawableBounds() const { Rectangle r; for (int i = getNumChildComponents(); --i >= 0;) if (const Drawable* const d = dynamic_cast (getChildComponent(i))) r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform()) : d->getDrawableBounds()); return r; } MarkerList* DrawableComposite::getMarkers (bool xAxis) { return xAxis ? &markersX : &markersY; } RelativeRectangle DrawableComposite::getContentArea() const { jassert (markersX.getNumMarkers() >= 2 && markersX.getMarker (0)->name == contentLeftMarkerName && markersX.getMarker (1)->name == contentRightMarkerName); jassert (markersY.getNumMarkers() >= 2 && markersY.getMarker (0)->name == contentTopMarkerName && markersY.getMarker (1)->name == contentBottomMarkerName); return RelativeRectangle (markersX.getMarker(0)->position, markersX.getMarker(1)->position, markersY.getMarker(0)->position, markersY.getMarker(1)->position); } void DrawableComposite::setContentArea (const RelativeRectangle& newArea) { markersX.setMarker (contentLeftMarkerName, newArea.left); markersX.setMarker (contentRightMarkerName, newArea.right); markersY.setMarker (contentTopMarkerName, newArea.top); markersY.setMarker (contentBottomMarkerName, newArea.bottom); } void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBounds) { if (bounds != newBounds) { bounds = newBounds; if (bounds.isDynamic()) { Drawable::Positioner* const p = new Drawable::Positioner (*this); setPositioner (p); p->apply(); } else { setPositioner (nullptr); recalculateCoordinates (nullptr); } } } void DrawableComposite::resetBoundingBoxToContentArea() { const RelativeRectangle content (getContentArea()); setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top), RelativePoint (content.right, content.top), RelativePoint (content.left, content.bottom))); } void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren() { const Rectangle activeArea (getDrawableBounds()); setContentArea (RelativeRectangle (RelativeCoordinate (activeArea.getX()), RelativeCoordinate (activeArea.getRight()), RelativeCoordinate (activeArea.getY()), RelativeCoordinate (activeArea.getBottom()))); resetBoundingBoxToContentArea(); } bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& pos) { bool ok = pos.addPoint (bounds.topLeft); ok = pos.addPoint (bounds.topRight) && ok; return pos.addPoint (bounds.bottomLeft) && ok; } void DrawableComposite::recalculateCoordinates (Expression::Scope* scope) { Point resolved[3]; bounds.resolveThreePoints (resolved, scope); const Rectangle content (getContentArea().resolve (scope)); AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].x, resolved[0].y, content.getRight(), content.getY(), resolved[1].x, resolved[1].y, content.getX(), content.getBottom(), resolved[2].x, resolved[2].y)); if (t.isSingularity()) t = AffineTransform::identity; setTransform (t); } void DrawableComposite::parentHierarchyChanged() { DrawableComposite* parent = getParent(); if (parent != nullptr) originRelativeToComponent = parent->originRelativeToComponent - getPosition(); } void DrawableComposite::childBoundsChanged (Component*) { updateBoundsToFitChildren(); } void DrawableComposite::childrenChanged() { updateBoundsToFitChildren(); } void DrawableComposite::updateBoundsToFitChildren() { if (! updateBoundsReentrant) { const ScopedValueSetter setter (updateBoundsReentrant, true, false); Rectangle childArea; for (int i = getNumChildComponents(); --i >= 0;) childArea = childArea.getUnion (getChildComponent(i)->getBoundsInParent()); const Point delta (childArea.getPosition()); childArea += getPosition(); if (childArea != getBounds()) { if (! delta.isOrigin()) { originRelativeToComponent -= delta; for (int i = getNumChildComponents(); --i >= 0;) if (Component* const c = getChildComponent(i)) c->setBounds (c->getBounds() - delta); } setBounds (childArea); } } } //============================================================================== const char* const DrawableComposite::contentLeftMarkerName = "left"; const char* const DrawableComposite::contentRightMarkerName = "right"; const char* const DrawableComposite::contentTopMarkerName = "top"; const char* const DrawableComposite::contentBottomMarkerName = "bottom"; //============================================================================== const Identifier DrawableComposite::valueTreeType ("Group"); const Identifier DrawableComposite::ValueTreeWrapper::topLeft ("topLeft"); const Identifier DrawableComposite::ValueTreeWrapper::topRight ("topRight"); const Identifier DrawableComposite::ValueTreeWrapper::bottomLeft ("bottomLeft"); const Identifier DrawableComposite::ValueTreeWrapper::childGroupTag ("Drawables"); const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagX ("MarkersX"); const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagY ("MarkersY"); //============================================================================== DrawableComposite::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) : ValueTreeWrapperBase (state_) { jassert (state.hasType (valueTreeType)); } ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const { return state.getChildWithName (childGroupTag); } ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager) { return state.getOrCreateChildWithName (childGroupTag, undoManager); } RelativeParallelogram DrawableComposite::ValueTreeWrapper::getBoundingBox() const { return RelativeParallelogram (state.getProperty (topLeft, "0, 0"), state.getProperty (topRight, "100, 0"), state.getProperty (bottomLeft, "0, 100")); } void DrawableComposite::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager) { state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager); state.setProperty (topRight, newBounds.topRight.toString(), undoManager); state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager); } void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoManager* undoManager) { const RelativeRectangle content (getContentArea()); setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top), RelativePoint (content.right, content.top), RelativePoint (content.left, content.bottom)), undoManager); } RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const { MarkerList::ValueTreeWrapper marksX (getMarkerList (true)); MarkerList::ValueTreeWrapper marksY (getMarkerList (false)); return RelativeRectangle (marksX.getMarker (marksX.getMarkerState (0)).position, marksX.getMarker (marksX.getMarkerState (1)).position, marksY.getMarker (marksY.getMarkerState (0)).position, marksY.getMarker (marksY.getMarkerState (1)).position); } void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager) { MarkerList::ValueTreeWrapper marksX (getMarkerListCreating (true, nullptr)); MarkerList::ValueTreeWrapper marksY (getMarkerListCreating (false, nullptr)); marksX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager); marksX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager); marksY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager); marksY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager); } MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const { return state.getChildWithName (xAxis ? markerGroupTagX : markerGroupTagY); } MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager) { return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager); } //============================================================================== void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder) { const ValueTreeWrapper wrapper (tree); setComponentID (wrapper.getID()); wrapper.getMarkerList (true).applyTo (markersX); wrapper.getMarkerList (false).applyTo (markersY); setBoundingBox (wrapper.getBoundingBox()); builder.updateChildComponents (*this, wrapper.getChildList()); } ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const { ValueTree tree (valueTreeType); ValueTreeWrapper v (tree); v.setID (getComponentID()); v.setBoundingBox (bounds, nullptr); ValueTree childList (v.getChildListCreating (nullptr)); for (int i = 0; i < getNumChildComponents(); ++i) { const Drawable* const d = dynamic_cast (getChildComponent(i)); jassert (d != nullptr); // You can't save a mix of Drawables and normal components! childList.addChild (d->createValueTree (imageProvider), -1, nullptr); } v.getMarkerListCreating (true, nullptr).readFrom (markersX, nullptr); v.getMarkerListCreating (false, nullptr).readFrom (markersY, nullptr); return tree; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.h000066400000000000000000000146041320201440200334560ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLECOMPOSITE_H_INCLUDED #define JUCE_DRAWABLECOMPOSITE_H_INCLUDED //============================================================================== /** A drawable object which acts as a container for a set of other Drawables. @see Drawable */ class JUCE_API DrawableComposite : public Drawable { public: //============================================================================== /** Creates a composite Drawable. */ DrawableComposite(); /** Creates a copy of a DrawableComposite. */ DrawableComposite (const DrawableComposite&); /** Destructor. */ ~DrawableComposite(); //============================================================================== /** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered. @see setContentArea */ void setBoundingBox (const RelativeParallelogram& newBoundingBox); /** Returns the parallelogram that defines the target position of the content rectangle when the drawable is rendered. @see setBoundingBox */ const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; } /** Changes the bounding box transform to match the content area, so that any sub-items will be drawn at their untransformed positions. */ void resetBoundingBoxToContentArea(); /** Returns the main content rectangle. The content area is actually defined by the markers named "left", "right", "top" and "bottom", but this method is a shortcut that returns them all at once. @see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName */ RelativeRectangle getContentArea() const; /** Changes the main content area. The content area is actually defined by the markers named "left", "right", "top" and "bottom", but this method is a shortcut that sets them all at once. @see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName */ void setContentArea (const RelativeRectangle& newArea); /** Resets the content area and the bounding transform to fit around the area occupied by the child components (ignoring any markers). */ void resetContentAreaAndBoundingBoxToFitChildren(); //============================================================================== /** The name of the marker that defines the left edge of the content area. */ static const char* const contentLeftMarkerName; /** The name of the marker that defines the right edge of the content area. */ static const char* const contentRightMarkerName; /** The name of the marker that defines the top edge of the content area. */ static const char* const contentTopMarkerName; /** The name of the marker that defines the bottom edge of the content area. */ static const char* const contentBottomMarkerName; //============================================================================== /** @internal */ Drawable* createCopy() const override; /** @internal */ void refreshFromValueTree (const ValueTree&, ComponentBuilder&); /** @internal */ ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const override; /** @internal */ static const Identifier valueTreeType; /** @internal */ Rectangle getDrawableBounds() const override; /** @internal */ void childBoundsChanged (Component*) override; /** @internal */ void childrenChanged() override; /** @internal */ void parentHierarchyChanged() override; /** @internal */ MarkerList* getMarkers (bool xAxis) override; //============================================================================== /** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */ class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase { public: ValueTreeWrapper (const ValueTree& state); ValueTree getChildList() const; ValueTree getChildListCreating (UndoManager* undoManager); RelativeParallelogram getBoundingBox() const; void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager); void resetBoundingBoxToContentArea (UndoManager* undoManager); RelativeRectangle getContentArea() const; void setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager); MarkerList::ValueTreeWrapper getMarkerList (bool xAxis) const; MarkerList::ValueTreeWrapper getMarkerListCreating (bool xAxis, UndoManager* undoManager); static const Identifier topLeft, topRight, bottomLeft; private: static const Identifier childGroupTag, markerGroupTagX, markerGroupTagY; }; private: //============================================================================== RelativeParallelogram bounds; MarkerList markersX, markersY; bool updateBoundsReentrant; friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); void recalculateCoordinates (Expression::Scope*); void updateBoundsToFitChildren(); DrawableComposite& operator= (const DrawableComposite&); JUCE_LEAK_DETECTOR (DrawableComposite) }; #endif // JUCE_DRAWABLECOMPOSITE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp000066400000000000000000000226021320201440200330660ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DrawableImage::DrawableImage() : opacity (1.0f), overlayColour (0x00000000) { bounds.topRight = RelativePoint (Point (1.0f, 0.0f)); bounds.bottomLeft = RelativePoint (Point (0.0f, 1.0f)); } DrawableImage::DrawableImage (const DrawableImage& other) : Drawable (other), image (other.image), opacity (other.opacity), overlayColour (other.overlayColour), bounds (other.bounds) { setBounds (other.getBounds()); } DrawableImage::~DrawableImage() { } //============================================================================== void DrawableImage::setImage (const Image& imageToUse) { image = imageToUse; setBounds (imageToUse.getBounds()); bounds.topLeft = RelativePoint (Point (0.0f, 0.0f)); bounds.topRight = RelativePoint (Point ((float) image.getWidth(), 0.0f)); bounds.bottomLeft = RelativePoint (Point (0.0f, (float) image.getHeight())); recalculateCoordinates (nullptr); repaint(); } void DrawableImage::setOpacity (const float newOpacity) { opacity = newOpacity; } void DrawableImage::setOverlayColour (Colour newOverlayColour) { overlayColour = newOverlayColour; } void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds) { if (bounds != newBounds) { bounds = newBounds; if (bounds.isDynamic()) { Drawable::Positioner* const p = new Drawable::Positioner (*this); setPositioner (p); p->apply(); } else { setPositioner (nullptr); recalculateCoordinates (nullptr); } } } //============================================================================== bool DrawableImage::registerCoordinates (RelativeCoordinatePositionerBase& pos) { bool ok = pos.addPoint (bounds.topLeft); ok = pos.addPoint (bounds.topRight) && ok; return pos.addPoint (bounds.bottomLeft) && ok; } void DrawableImage::recalculateCoordinates (Expression::Scope* scope) { if (image.isValid()) { Point resolved[3]; bounds.resolveThreePoints (resolved, scope); const Point tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth()); const Point bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight()); AffineTransform t (AffineTransform::fromTargetPoints (resolved[0].x, resolved[0].y, tr.x, tr.y, bl.x, bl.y)); if (t.isSingularity()) t = AffineTransform::identity; setTransform (t); } } //============================================================================== void DrawableImage::paint (Graphics& g) { if (image.isValid()) { if (opacity > 0.0f && ! overlayColour.isOpaque()) { g.setOpacity (opacity); g.drawImageAt (image, 0, 0, false); } if (! overlayColour.isTransparent()) { g.setColour (overlayColour.withMultipliedAlpha (opacity)); g.drawImageAt (image, 0, 0, true); } } } Rectangle DrawableImage::getDrawableBounds() const { return image.getBounds().toFloat(); } bool DrawableImage::hitTest (int x, int y) { return Drawable::hitTest (x, y) && image.isValid() && image.getPixelAt (x, y).getAlpha() >= 127; } Drawable* DrawableImage::createCopy() const { return new DrawableImage (*this); } //============================================================================== const Identifier DrawableImage::valueTreeType ("Image"); const Identifier DrawableImage::ValueTreeWrapper::opacity ("opacity"); const Identifier DrawableImage::ValueTreeWrapper::overlay ("overlay"); const Identifier DrawableImage::ValueTreeWrapper::image ("image"); const Identifier DrawableImage::ValueTreeWrapper::topLeft ("topLeft"); const Identifier DrawableImage::ValueTreeWrapper::topRight ("topRight"); const Identifier DrawableImage::ValueTreeWrapper::bottomLeft ("bottomLeft"); //============================================================================== DrawableImage::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) : ValueTreeWrapperBase (state_) { jassert (state.hasType (valueTreeType)); } var DrawableImage::ValueTreeWrapper::getImageIdentifier() const { return state [image]; } Value DrawableImage::ValueTreeWrapper::getImageIdentifierValue (UndoManager* undoManager) { return state.getPropertyAsValue (image, undoManager); } void DrawableImage::ValueTreeWrapper::setImageIdentifier (const var& newIdentifier, UndoManager* undoManager) { state.setProperty (image, newIdentifier, undoManager); } float DrawableImage::ValueTreeWrapper::getOpacity() const { return (float) state.getProperty (opacity, 1.0); } Value DrawableImage::ValueTreeWrapper::getOpacityValue (UndoManager* undoManager) { if (! state.hasProperty (opacity)) state.setProperty (opacity, 1.0, undoManager); return state.getPropertyAsValue (opacity, undoManager); } void DrawableImage::ValueTreeWrapper::setOpacity (float newOpacity, UndoManager* undoManager) { state.setProperty (opacity, newOpacity, undoManager); } Colour DrawableImage::ValueTreeWrapper::getOverlayColour() const { return Colour::fromString (state [overlay].toString()); } void DrawableImage::ValueTreeWrapper::setOverlayColour (Colour newColour, UndoManager* undoManager) { if (newColour.isTransparent()) state.removeProperty (overlay, undoManager); else state.setProperty (overlay, String::toHexString ((int) newColour.getARGB()), undoManager); } Value DrawableImage::ValueTreeWrapper::getOverlayColourValue (UndoManager* undoManager) { return state.getPropertyAsValue (overlay, undoManager); } RelativeParallelogram DrawableImage::ValueTreeWrapper::getBoundingBox() const { return RelativeParallelogram (state.getProperty (topLeft, "0, 0"), state.getProperty (topRight, "100, 0"), state.getProperty (bottomLeft, "0, 100")); } void DrawableImage::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager) { state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager); state.setProperty (topRight, newBounds.topRight.toString(), undoManager); state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager); } //============================================================================== void DrawableImage::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder) { const ValueTreeWrapper controller (tree); setComponentID (controller.getID()); const float newOpacity = controller.getOpacity(); const Colour newOverlayColour (controller.getOverlayColour()); Image newImage; const var imageIdentifier (controller.getImageIdentifier()); jassert (builder.getImageProvider() != 0 || imageIdentifier.isVoid()); // if you're using images, you need to provide something that can load and save them! if (builder.getImageProvider() != nullptr) newImage = builder.getImageProvider()->getImageForIdentifier (imageIdentifier); const RelativeParallelogram newBounds (controller.getBoundingBox()); if (bounds != newBounds || newOpacity != opacity || overlayColour != newOverlayColour || image != newImage) { repaint(); opacity = newOpacity; overlayColour = newOverlayColour; if (image != newImage) setImage (newImage); setBoundingBox (newBounds); } } ValueTree DrawableImage::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const { ValueTree tree (valueTreeType); ValueTreeWrapper v (tree); v.setID (getComponentID()); v.setOpacity (opacity, nullptr); v.setOverlayColour (overlayColour, nullptr); v.setBoundingBox (bounds, nullptr); if (image.isValid()) { jassert (imageProvider != nullptr); // if you're using images, you need to provide something that can load and save them! if (imageProvider != nullptr) v.setImageIdentifier (imageProvider->getIdentifierForImage (image), nullptr); } return tree; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h000066400000000000000000000121011320201440200325240ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLEIMAGE_H_INCLUDED #define JUCE_DRAWABLEIMAGE_H_INCLUDED //============================================================================== /** A drawable object which is a bitmap image. @see Drawable */ class JUCE_API DrawableImage : public Drawable { public: //============================================================================== DrawableImage(); DrawableImage (const DrawableImage&); /** Destructor. */ ~DrawableImage(); //============================================================================== /** Sets the image that this drawable will render. */ void setImage (const Image& imageToUse); /** Returns the current image. */ const Image& getImage() const noexcept { return image; } /** Sets the opacity to use when drawing the image. */ void setOpacity (float newOpacity); /** Returns the image's opacity. */ float getOpacity() const noexcept { return opacity; } /** Sets a colour to draw over the image's alpha channel. By default this is transparent so isn't drawn, but if you set a non-transparent colour here, then it will be overlaid on the image, using the image's alpha channel as a mask. This is handy for doing things like darkening or lightening an image by overlaying it with semi-transparent black or white. */ void setOverlayColour (Colour newOverlayColour); /** Returns the overlay colour. */ Colour getOverlayColour() const noexcept { return overlayColour; } /** Sets the bounding box within which the image should be displayed. */ void setBoundingBox (const RelativeParallelogram& newBounds); /** Returns the position to which the image's top-left corner should be remapped in the target coordinate space when rendering this object. @see setTransform */ const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; } //============================================================================== /** @internal */ void paint (Graphics&) override; /** @internal */ bool hitTest (int x, int y) override; /** @internal */ Drawable* createCopy() const override; /** @internal */ Rectangle getDrawableBounds() const override; /** @internal */ void refreshFromValueTree (const ValueTree& tree, ComponentBuilder&); /** @internal */ ValueTree createValueTree (ComponentBuilder::ImageProvider*) const override; /** @internal */ static const Identifier valueTreeType; //============================================================================== /** Internally-used class for wrapping a DrawableImage's state into a ValueTree. */ class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase { public: ValueTreeWrapper (const ValueTree& state); var getImageIdentifier() const; void setImageIdentifier (const var&, UndoManager*); Value getImageIdentifierValue (UndoManager*); float getOpacity() const; void setOpacity (float newOpacity, UndoManager*); Value getOpacityValue (UndoManager*); Colour getOverlayColour() const; void setOverlayColour (Colour newColour, UndoManager*); Value getOverlayColourValue (UndoManager*); RelativeParallelogram getBoundingBox() const; void setBoundingBox (const RelativeParallelogram&, UndoManager*); static const Identifier opacity, overlay, image, topLeft, topRight, bottomLeft; }; private: //============================================================================== Image image; float opacity; Colour overlayColour; RelativeParallelogram bounds; friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); void recalculateCoordinates (Expression::Scope*); DrawableImage& operator= (const DrawableImage&); JUCE_LEAK_DETECTOR (DrawableImage) }; #endif // JUCE_DRAWABLEIMAGE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp000066400000000000000000000474041320201440200327470ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DrawablePath::DrawablePath() { } DrawablePath::DrawablePath (const DrawablePath& other) : DrawableShape (other) { if (other.relativePath != nullptr) setPath (*other.relativePath); else setPath (other.path); } DrawablePath::~DrawablePath() { } Drawable* DrawablePath::createCopy() const { return new DrawablePath (*this); } //============================================================================== void DrawablePath::setPath (const Path& newPath) { path = newPath; pathChanged(); } const Path& DrawablePath::getPath() const { return path; } const Path& DrawablePath::getStrokePath() const { return strokePath; } void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, Expression::Scope* scope) { Path newPath; newRelativePath.createPath (newPath, scope); if (path != newPath) { path.swapWithPath (newPath); pathChanged(); } } //============================================================================== class DrawablePath::RelativePositioner : public RelativeCoordinatePositionerBase { public: RelativePositioner (DrawablePath& comp) : RelativeCoordinatePositionerBase (comp), owner (comp) { } bool registerCoordinates() override { bool ok = true; jassert (owner.relativePath != nullptr); const RelativePointPath& path = *owner.relativePath; for (int i = 0; i < path.elements.size(); ++i) { RelativePointPath::ElementBase* const e = path.elements.getUnchecked(i); int numPoints; RelativePoint* const points = e->getControlPoints (numPoints); for (int j = numPoints; --j >= 0;) ok = addPoint (points[j]) && ok; } return ok; } void applyToComponentBounds() override { jassert (owner.relativePath != nullptr); ComponentScope scope (getComponent()); owner.applyRelativePath (*owner.relativePath, &scope); } void applyNewBounds (const Rectangle&) override { jassertfalse; // drawables can't be resized directly! } private: DrawablePath& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner) }; void DrawablePath::setPath (const RelativePointPath& newRelativePath) { if (newRelativePath.containsAnyDynamicPoints()) { if (relativePath == nullptr || newRelativePath != *relativePath) { relativePath = new RelativePointPath (newRelativePath); RelativePositioner* const p = new RelativePositioner (*this); setPositioner (p); p->apply(); } } else { relativePath = nullptr; applyRelativePath (newRelativePath, nullptr); } } //============================================================================== const Identifier DrawablePath::valueTreeType ("Path"); const Identifier DrawablePath::ValueTreeWrapper::nonZeroWinding ("nonZeroWinding"); const Identifier DrawablePath::ValueTreeWrapper::point1 ("p1"); const Identifier DrawablePath::ValueTreeWrapper::point2 ("p2"); const Identifier DrawablePath::ValueTreeWrapper::point3 ("p3"); //============================================================================== DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) : FillAndStrokeState (state_) { jassert (state.hasType (valueTreeType)); } ValueTree DrawablePath::ValueTreeWrapper::getPathState() { return state.getOrCreateChildWithName (path, nullptr); } bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const { return state [nonZeroWinding]; } void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager) { state.setProperty (nonZeroWinding, b, undoManager); } void DrawablePath::ValueTreeWrapper::readFrom (const RelativePointPath& p, UndoManager* undoManager) { setUsesNonZeroWinding (p.usesNonZeroWinding, undoManager); ValueTree pathTree (getPathState()); pathTree.removeAllChildren (undoManager); for (int i = 0; i < p.elements.size(); ++i) pathTree.addChild (p.elements.getUnchecked(i)->createTree(), -1, undoManager); } void DrawablePath::ValueTreeWrapper::writeTo (RelativePointPath& p) const { p.usesNonZeroWinding = usesNonZeroWinding(); RelativePoint points[3]; const ValueTree pathTree (state.getChildWithName (path)); const int num = pathTree.getNumChildren(); for (int i = 0; i < num; ++i) { const Element e (pathTree.getChild(i)); const int numCps = e.getNumControlPoints(); for (int j = 0; j < numCps; ++j) points[j] = e.getControlPoint (j); RelativePointPath::ElementBase* newElement = nullptr; const Identifier t (e.getType()); if (t == Element::startSubPathElement) newElement = new RelativePointPath::StartSubPath (points[0]); else if (t == Element::closeSubPathElement) newElement = new RelativePointPath::CloseSubPath(); else if (t == Element::lineToElement) newElement = new RelativePointPath::LineTo (points[0]); else if (t == Element::quadraticToElement) newElement = new RelativePointPath::QuadraticTo (points[0], points[1]); else if (t == Element::cubicToElement) newElement = new RelativePointPath::CubicTo (points[0], points[1], points[2]); else jassertfalse; p.addElement (newElement); } } //============================================================================== const Identifier DrawablePath::ValueTreeWrapper::Element::mode ("mode"); const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move"); const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close"); const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line"); const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad"); const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic"); const char* DrawablePath::ValueTreeWrapper::Element::cornerMode = "corner"; const char* DrawablePath::ValueTreeWrapper::Element::roundedMode = "round"; const char* DrawablePath::ValueTreeWrapper::Element::symmetricMode = "symm"; DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_) : state (state_) { } DrawablePath::ValueTreeWrapper::Element::~Element() { } DrawablePath::ValueTreeWrapper DrawablePath::ValueTreeWrapper::Element::getParent() const { return ValueTreeWrapper (state.getParent().getParent()); } DrawablePath::ValueTreeWrapper::Element DrawablePath::ValueTreeWrapper::Element::getPreviousElement() const { return Element (state.getSibling (-1)); } int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const noexcept { const Identifier i (state.getType()); if (i == startSubPathElement || i == lineToElement) return 1; if (i == quadraticToElement) return 2; if (i == cubicToElement) return 3; return 0; } RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const { jassert (index >= 0 && index < getNumControlPoints()); return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString()); } Value DrawablePath::ValueTreeWrapper::Element::getControlPointValue (int index, UndoManager* undoManager) { jassert (index >= 0 && index < getNumControlPoints()); return state.getPropertyAsValue (index == 0 ? point1 : (index == 1 ? point2 : point3), undoManager); } void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager) { jassert (index >= 0 && index < getNumControlPoints()); state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager); } RelativePoint DrawablePath::ValueTreeWrapper::Element::getStartPoint() const { const Identifier i (state.getType()); if (i == startSubPathElement) return getControlPoint (0); jassert (i == lineToElement || i == quadraticToElement || i == cubicToElement || i == closeSubPathElement); return getPreviousElement().getEndPoint(); } RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const { const Identifier i (state.getType()); if (i == startSubPathElement || i == lineToElement) return getControlPoint (0); if (i == quadraticToElement) return getControlPoint (1); if (i == cubicToElement) return getControlPoint (2); jassert (i == closeSubPathElement); return RelativePoint(); } float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::Scope* scope) const { const Identifier i (state.getType()); if (i == lineToElement || i == closeSubPathElement) return getEndPoint().resolve (scope).getDistanceFrom (getStartPoint().resolve (scope)); if (i == cubicToElement) { Path p; p.startNewSubPath (getStartPoint().resolve (scope)); p.cubicTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope), getControlPoint (2).resolve (scope)); return p.getLength(); } if (i == quadraticToElement) { Path p; p.startNewSubPath (getStartPoint().resolve (scope)); p.quadraticTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope)); return p.getLength(); } jassert (i == startSubPathElement); return 0; } String DrawablePath::ValueTreeWrapper::Element::getModeOfEndPoint() const { return state [mode].toString(); } void DrawablePath::ValueTreeWrapper::Element::setModeOfEndPoint (const String& newMode, UndoManager* undoManager) { if (state.hasType (cubicToElement)) state.setProperty (mode, newMode, undoManager); } void DrawablePath::ValueTreeWrapper::Element::convertToLine (UndoManager* undoManager) { const Identifier i (state.getType()); if (i == quadraticToElement || i == cubicToElement) { ValueTree newState (lineToElement); Element e (newState); e.setControlPoint (0, getEndPoint(), undoManager); state = newState; } } void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::Scope* scope, UndoManager* undoManager) { const Identifier i (state.getType()); if (i == lineToElement || i == quadraticToElement) { ValueTree newState (cubicToElement); Element e (newState); const RelativePoint start (getStartPoint()); const RelativePoint end (getEndPoint()); const Point startResolved (start.resolve (scope)); const Point endResolved (end.resolve (scope)); e.setControlPoint (0, startResolved + (endResolved - startResolved) * 0.3f, undoManager); e.setControlPoint (1, startResolved + (endResolved - startResolved) * 0.7f, undoManager); e.setControlPoint (2, end, undoManager); state = newState; } } void DrawablePath::ValueTreeWrapper::Element::convertToPathBreak (UndoManager* undoManager) { const Identifier i (state.getType()); if (i != startSubPathElement) { ValueTree newState (startSubPathElement); Element e (newState); e.setControlPoint (0, getEndPoint(), undoManager); state = newState; } } namespace DrawablePathHelpers { static Point findCubicSubdivisionPoint (float proportion, const Point points[4]) { const Point mid1 (points[0] + (points[1] - points[0]) * proportion), mid2 (points[1] + (points[2] - points[1]) * proportion), mid3 (points[2] + (points[3] - points[2]) * proportion); const Point newCp1 (mid1 + (mid2 - mid1) * proportion), newCp2 (mid2 + (mid3 - mid2) * proportion); return newCp1 + (newCp2 - newCp1) * proportion; } static Point findQuadraticSubdivisionPoint (float proportion, const Point points[3]) { const Point mid1 (points[0] + (points[1] - points[0]) * proportion), mid2 (points[1] + (points[2] - points[1]) * proportion); return mid1 + (mid2 - mid1) * proportion; } } float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (Point targetPoint, Expression::Scope* scope) const { using namespace DrawablePathHelpers; const Identifier pointType (state.getType()); float bestProp = 0; if (pointType == cubicToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) }; float bestDistance = std::numeric_limits::max(); for (int i = 110; --i >= 0;) { float prop = i > 10 ? ((i - 10) / 100.0f) : (bestProp + ((i - 5) / 1000.0f)); const Point centre (findCubicSubdivisionPoint (prop, points)); const float distance = centre.getDistanceFrom (targetPoint); if (distance < bestDistance) { bestProp = prop; bestDistance = distance; } } } else if (pointType == quadraticToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) }; float bestDistance = std::numeric_limits::max(); for (int i = 110; --i >= 0;) { float prop = i > 10 ? ((i - 10) / 100.0f) : (bestProp + ((i - 5) / 1000.0f)); const Point centre (findQuadraticSubdivisionPoint ((float) prop, points)); const float distance = centre.getDistanceFrom (targetPoint); if (distance < bestDistance) { bestProp = prop; bestDistance = distance; } } } else if (pointType == lineToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); const Line line (rp1.resolve (scope), rp2.resolve (scope)); bestProp = line.findNearestProportionalPositionTo (targetPoint); } return bestProp; } ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (Point targetPoint, Expression::Scope* scope, UndoManager* undoManager) { ValueTree newTree; const Identifier pointType (state.getType()); if (pointType == cubicToElement) { float bestProp = findProportionAlongLine (targetPoint, scope); RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) }; const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), mid2 (points[1] + (points[2] - points[1]) * bestProp), mid3 (points[2] + (points[3] - points[2]) * bestProp); const Point newCp1 (mid1 + (mid2 - mid1) * bestProp), newCp2 (mid2 + (mid3 - mid2) * bestProp); const Point newCentre (newCp1 + (newCp2 - newCp1) * bestProp); setControlPoint (0, mid1, undoManager); setControlPoint (1, newCp1, undoManager); setControlPoint (2, newCentre, undoManager); setModeOfEndPoint (roundedMode, undoManager); Element newElement (newTree = ValueTree (cubicToElement)); newElement.setControlPoint (0, newCp2, nullptr); newElement.setControlPoint (1, mid3, nullptr); newElement.setControlPoint (2, rp4, nullptr); state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); } else if (pointType == quadraticToElement) { float bestProp = findProportionAlongLine (targetPoint, scope); RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) }; const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), mid2 (points[1] + (points[2] - points[1]) * bestProp); const Point newCentre (mid1 + (mid2 - mid1) * bestProp); setControlPoint (0, mid1, undoManager); setControlPoint (1, newCentre, undoManager); setModeOfEndPoint (roundedMode, undoManager); Element newElement (newTree = ValueTree (quadraticToElement)); newElement.setControlPoint (0, mid2, nullptr); newElement.setControlPoint (1, rp3, nullptr); state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); } else if (pointType == lineToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); const Line line (rp1.resolve (scope), rp2.resolve (scope)); const Point newPoint (line.findNearestPointTo (targetPoint)); setControlPoint (0, newPoint, undoManager); Element newElement (newTree = ValueTree (lineToElement)); newElement.setControlPoint (0, rp2, nullptr); state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); } else if (pointType == closeSubPathElement) { } return newTree; } void DrawablePath::ValueTreeWrapper::Element::removePoint (UndoManager* undoManager) { state.getParent().removeChild (state, undoManager); } //============================================================================== void DrawablePath::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder) { ValueTreeWrapper v (tree); setComponentID (v.getID()); refreshFillTypes (v, builder.getImageProvider()); setStrokeType (v.getStrokeType()); RelativePointPath newRelativePath; v.writeTo (newRelativePath); setPath (newRelativePath); } ValueTree DrawablePath::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const { ValueTree tree (valueTreeType); ValueTreeWrapper v (tree); v.setID (getComponentID()); writeTo (v, imageProvider, nullptr); if (relativePath != nullptr) v.readFrom (*relativePath, nullptr); else v.readFrom (RelativePointPath (path), nullptr); return tree; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawablePath.h000066400000000000000000000125451320201440200324120ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLEPATH_H_INCLUDED #define JUCE_DRAWABLEPATH_H_INCLUDED //============================================================================== /** A drawable object which renders a filled or outlined shape. For details on how to change the fill and stroke, see the DrawableShape class. @see Drawable, DrawableShape */ class JUCE_API DrawablePath : public DrawableShape { public: //============================================================================== /** Creates a DrawablePath. */ DrawablePath(); DrawablePath (const DrawablePath&); /** Destructor. */ ~DrawablePath(); //============================================================================== /** Changes the path that will be drawn. @see setFillColour, setStrokeType */ void setPath (const Path& newPath); /** Sets the path using a RelativePointPath. Calling this will set up a Component::Positioner to automatically update the path if any of the points in the source path are dynamic. */ void setPath (const RelativePointPath& newPath); /** Returns the current path. */ const Path& getPath() const; /** Returns the current path for the outline. */ const Path& getStrokePath() const; //============================================================================== /** @internal */ Drawable* createCopy() const; /** @internal */ void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder); /** @internal */ ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const; /** @internal */ static const Identifier valueTreeType; //============================================================================== /** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */ class ValueTreeWrapper : public DrawableShape::FillAndStrokeState { public: ValueTreeWrapper (const ValueTree& state); bool usesNonZeroWinding() const; void setUsesNonZeroWinding (bool b, UndoManager* undoManager); class Element { public: explicit Element (const ValueTree& state); ~Element(); const Identifier getType() const noexcept { return state.getType(); } int getNumControlPoints() const noexcept; RelativePoint getControlPoint (int index) const; Value getControlPointValue (int index, UndoManager*); RelativePoint getStartPoint() const; RelativePoint getEndPoint() const; void setControlPoint (int index, const RelativePoint& point, UndoManager*); float getLength (Expression::Scope*) const; ValueTreeWrapper getParent() const; Element getPreviousElement() const; String getModeOfEndPoint() const; void setModeOfEndPoint (const String& newMode, UndoManager*); void convertToLine (UndoManager*); void convertToCubic (Expression::Scope*, UndoManager*); void convertToPathBreak (UndoManager* undoManager); ValueTree insertPoint (Point targetPoint, Expression::Scope*, UndoManager*); void removePoint (UndoManager* undoManager); float findProportionAlongLine (Point targetPoint, Expression::Scope*) const; static const Identifier mode, startSubPathElement, closeSubPathElement, lineToElement, quadraticToElement, cubicToElement; static const char* cornerMode; static const char* roundedMode; static const char* symmetricMode; ValueTree state; }; ValueTree getPathState(); void readFrom (const RelativePointPath& path, UndoManager* undoManager); void writeTo (RelativePointPath& path) const; static const Identifier nonZeroWinding, point1, point2, point3; }; private: //============================================================================== ScopedPointer relativePath; class RelativePositioner; friend class RelativePositioner; void applyRelativePath (const RelativePointPath&, Expression::Scope*); DrawablePath& operator= (const DrawablePath&); JUCE_LEAK_DETECTOR (DrawablePath) }; #endif // JUCE_DRAWABLEPATH_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp000066400000000000000000000137441320201440200337570ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DrawableRectangle::DrawableRectangle() { } DrawableRectangle::DrawableRectangle (const DrawableRectangle& other) : DrawableShape (other), bounds (other.bounds), cornerSize (other.cornerSize) { } DrawableRectangle::~DrawableRectangle() { } Drawable* DrawableRectangle::createCopy() const { return new DrawableRectangle (*this); } //============================================================================== void DrawableRectangle::setRectangle (const RelativeParallelogram& newBounds) { if (bounds != newBounds) { bounds = newBounds; rebuildPath(); } } void DrawableRectangle::setCornerSize (const RelativePoint& newSize) { if (cornerSize != newSize) { cornerSize = newSize; rebuildPath(); } } void DrawableRectangle::rebuildPath() { if (bounds.isDynamic() || cornerSize.isDynamic()) { Drawable::Positioner* const p = new Drawable::Positioner (*this); setPositioner (p); p->apply(); } else { setPositioner (nullptr); recalculateCoordinates (nullptr); } } bool DrawableRectangle::registerCoordinates (RelativeCoordinatePositionerBase& pos) { bool ok = pos.addPoint (bounds.topLeft); ok = pos.addPoint (bounds.topRight) && ok; ok = pos.addPoint (bounds.bottomLeft) && ok; return pos.addPoint (cornerSize) && ok; } void DrawableRectangle::recalculateCoordinates (Expression::Scope* scope) { Point points[3]; bounds.resolveThreePoints (points, scope); const float cornerSizeX = (float) cornerSize.x.resolve (scope); const float cornerSizeY = (float) cornerSize.y.resolve (scope); const float w = Line (points[0], points[1]).getLength(); const float h = Line (points[0], points[2]).getLength(); Path newPath; if (cornerSizeX > 0 && cornerSizeY > 0) newPath.addRoundedRectangle (0, 0, w, h, cornerSizeX, cornerSizeY); else newPath.addRectangle (0, 0, w, h); newPath.applyTransform (AffineTransform::fromTargetPoints (0, 0, points[0].x, points[0].y, w, 0, points[1].x, points[1].y, 0, h, points[2].x, points[2].y)); if (path != newPath) { path.swapWithPath (newPath); pathChanged(); } } //============================================================================== const Identifier DrawableRectangle::valueTreeType ("Rectangle"); const Identifier DrawableRectangle::ValueTreeWrapper::topLeft ("topLeft"); const Identifier DrawableRectangle::ValueTreeWrapper::topRight ("topRight"); const Identifier DrawableRectangle::ValueTreeWrapper::bottomLeft ("bottomLeft"); const Identifier DrawableRectangle::ValueTreeWrapper::cornerSize ("cornerSize"); //============================================================================== DrawableRectangle::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) : FillAndStrokeState (state_) { jassert (state.hasType (valueTreeType)); } RelativeParallelogram DrawableRectangle::ValueTreeWrapper::getRectangle() const { return RelativeParallelogram (state.getProperty (topLeft, "0, 0"), state.getProperty (topRight, "100, 0"), state.getProperty (bottomLeft, "0, 100")); } void DrawableRectangle::ValueTreeWrapper::setRectangle (const RelativeParallelogram& newBounds, UndoManager* undoManager) { state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager); state.setProperty (topRight, newBounds.topRight.toString(), undoManager); state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager); } void DrawableRectangle::ValueTreeWrapper::setCornerSize (const RelativePoint& newSize, UndoManager* undoManager) { state.setProperty (cornerSize, newSize.toString(), undoManager); } RelativePoint DrawableRectangle::ValueTreeWrapper::getCornerSize() const { return RelativePoint (state [cornerSize]); } Value DrawableRectangle::ValueTreeWrapper::getCornerSizeValue (UndoManager* undoManager) { return state.getPropertyAsValue (cornerSize, undoManager); } //============================================================================== void DrawableRectangle::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder) { ValueTreeWrapper v (tree); setComponentID (v.getID()); refreshFillTypes (v, builder.getImageProvider()); setStrokeType (v.getStrokeType()); setRectangle (v.getRectangle()); setCornerSize (v.getCornerSize()); } ValueTree DrawableRectangle::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const { ValueTree tree (valueTreeType); ValueTreeWrapper v (tree); v.setID (getComponentID()); writeTo (v, imageProvider, nullptr); v.setRectangle (bounds, nullptr); v.setCornerSize (cornerSize, nullptr); return tree; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.h000066400000000000000000000072611320201440200334210ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLERECTANGLE_H_INCLUDED #define JUCE_DRAWABLERECTANGLE_H_INCLUDED //============================================================================== /** A Drawable object which draws a rectangle. For details on how to change the fill and stroke, see the DrawableShape class. @see Drawable, DrawableShape */ class JUCE_API DrawableRectangle : public DrawableShape { public: //============================================================================== DrawableRectangle(); DrawableRectangle (const DrawableRectangle&); /** Destructor. */ ~DrawableRectangle(); //============================================================================== /** Sets the rectangle's bounds. */ void setRectangle (const RelativeParallelogram& newBounds); /** Returns the rectangle's bounds. */ const RelativeParallelogram& getRectangle() const noexcept { return bounds; } /** Returns the corner size to be used. */ const RelativePoint& getCornerSize() const noexcept { return cornerSize; } /** Sets a new corner size for the rectangle */ void setCornerSize (const RelativePoint& newSize); //============================================================================== /** @internal */ Drawable* createCopy() const; /** @internal */ void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder); /** @internal */ ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const; /** @internal */ static const Identifier valueTreeType; //============================================================================== /** Internally-used class for wrapping a DrawableRectangle's state into a ValueTree. */ class ValueTreeWrapper : public DrawableShape::FillAndStrokeState { public: ValueTreeWrapper (const ValueTree& state); RelativeParallelogram getRectangle() const; void setRectangle (const RelativeParallelogram& newBounds, UndoManager*); void setCornerSize (const RelativePoint& cornerSize, UndoManager*); RelativePoint getCornerSize() const; Value getCornerSizeValue (UndoManager*); static const Identifier topLeft, topRight, bottomLeft, cornerSize; }; private: friend class Drawable::Positioner; RelativeParallelogram bounds; RelativePoint cornerSize; void rebuildPath(); bool registerCoordinates (RelativeCoordinatePositionerBase&); void recalculateCoordinates (Expression::Scope*); DrawableRectangle& operator= (const DrawableRectangle&); JUCE_LEAK_DETECTOR (DrawableRectangle) }; #endif // JUCE_DRAWABLERECTANGLE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp000066400000000000000000000412111320201440200331010ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DrawableShape::DrawableShape() : strokeType (0.0f), mainFill (Colours::black), strokeFill (Colours::black) { } DrawableShape::DrawableShape (const DrawableShape& other) : Drawable (other), strokeType (other.strokeType), mainFill (other.mainFill), strokeFill (other.strokeFill) { } DrawableShape::~DrawableShape() { } //============================================================================== class DrawableShape::RelativePositioner : public RelativeCoordinatePositionerBase { public: RelativePositioner (DrawableShape& comp, const DrawableShape::RelativeFillType& f, bool isMain) : RelativeCoordinatePositionerBase (comp), owner (comp), fill (f), isMainFill (isMain) { } bool registerCoordinates() override { bool ok = addPoint (fill.gradientPoint1); ok = addPoint (fill.gradientPoint2) && ok; return addPoint (fill.gradientPoint3) && ok; } void applyToComponentBounds() override { ComponentScope scope (owner); if (isMainFill ? owner.mainFill.recalculateCoords (&scope) : owner.strokeFill.recalculateCoords (&scope)) owner.repaint(); } void applyNewBounds (const Rectangle&) override { jassertfalse; // drawables can't be resized directly! } private: DrawableShape& owner; const DrawableShape::RelativeFillType fill; const bool isMainFill; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner) }; void DrawableShape::setFill (const FillType& newFill) { setFill (RelativeFillType (newFill)); } void DrawableShape::setStrokeFill (const FillType& newFill) { setStrokeFill (RelativeFillType (newFill)); } void DrawableShape::setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill, ScopedPointer& pos) { if (fill != newFill) { fill = newFill; pos = nullptr; if (fill.isDynamic()) { pos = new RelativePositioner (*this, fill, true); pos->apply(); } else { fill.recalculateCoords (nullptr); } repaint(); } } void DrawableShape::setFill (const RelativeFillType& newFill) { setFillInternal (mainFill, newFill, mainFillPositioner); } void DrawableShape::setStrokeFill (const RelativeFillType& newFill) { setFillInternal (strokeFill, newFill, strokeFillPositioner); } void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType) { if (strokeType != newStrokeType) { strokeType = newStrokeType; strokeChanged(); } } void DrawableShape::setStrokeThickness (const float newThickness) { setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle())); } bool DrawableShape::isStrokeVisible() const noexcept { return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.fill.isInvisible(); } void DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, ComponentBuilder::ImageProvider* imageProvider) { setFill (newState.getFill (FillAndStrokeState::fill, imageProvider)); setStrokeFill (newState.getFill (FillAndStrokeState::stroke, imageProvider)); } void DrawableShape::writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const { state.setFill (FillAndStrokeState::fill, mainFill, imageProvider, undoManager); state.setFill (FillAndStrokeState::stroke, strokeFill, imageProvider, undoManager); state.setStrokeType (strokeType, undoManager); } //============================================================================== void DrawableShape::paint (Graphics& g) { transformContextToCorrectOrigin (g); g.setFillType (mainFill.fill); g.fillPath (path); if (isStrokeVisible()) { g.setFillType (strokeFill.fill); g.fillPath (strokePath); } } void DrawableShape::pathChanged() { strokeChanged(); } void DrawableShape::strokeChanged() { strokePath.clear(); strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f); setBoundsToEnclose (getDrawableBounds()); repaint(); } Rectangle DrawableShape::getDrawableBounds() const { if (isStrokeVisible()) return strokePath.getBounds(); return path.getBounds(); } bool DrawableShape::hitTest (int x, int y) { bool allowsClicksOnThisComponent, allowsClicksOnChildComponents; getInterceptsMouseClicks (allowsClicksOnThisComponent, allowsClicksOnChildComponents); if (! allowsClicksOnThisComponent) return false; const float globalX = (float) (x - originRelativeToComponent.x); const float globalY = (float) (y - originRelativeToComponent.y); return path.contains (globalX, globalY) || (isStrokeVisible() && strokePath.contains (globalX, globalY)); } //============================================================================== DrawableShape::RelativeFillType::RelativeFillType() { } DrawableShape::RelativeFillType::RelativeFillType (const FillType& fill_) : fill (fill_) { if (fill.isGradient()) { const ColourGradient& g = *fill.gradient; gradientPoint1 = g.point1.transformedBy (fill.transform); gradientPoint2 = g.point2.transformedBy (fill.transform); gradientPoint3 = Point (g.point1.x + g.point2.y - g.point1.y, g.point1.y + g.point1.x - g.point2.x) .transformedBy (fill.transform); fill.transform = AffineTransform::identity; } } DrawableShape::RelativeFillType::RelativeFillType (const RelativeFillType& other) : fill (other.fill), gradientPoint1 (other.gradientPoint1), gradientPoint2 (other.gradientPoint2), gradientPoint3 (other.gradientPoint3) { } DrawableShape::RelativeFillType& DrawableShape::RelativeFillType::operator= (const RelativeFillType& other) { fill = other.fill; gradientPoint1 = other.gradientPoint1; gradientPoint2 = other.gradientPoint2; gradientPoint3 = other.gradientPoint3; return *this; } bool DrawableShape::RelativeFillType::operator== (const RelativeFillType& other) const { return fill == other.fill && ((! fill.isGradient()) || (gradientPoint1 == other.gradientPoint1 && gradientPoint2 == other.gradientPoint2 && gradientPoint3 == other.gradientPoint3)); } bool DrawableShape::RelativeFillType::operator!= (const RelativeFillType& other) const { return ! operator== (other); } bool DrawableShape::RelativeFillType::recalculateCoords (Expression::Scope* scope) { if (fill.isGradient()) { const Point g1 (gradientPoint1.resolve (scope)); const Point g2 (gradientPoint2.resolve (scope)); AffineTransform t; ColourGradient& g = *fill.gradient; if (g.isRadial) { const Point g3 (gradientPoint3.resolve (scope)); const Point g3Source (g1.x + g2.y - g1.y, g1.y + g1.x - g2.x); t = AffineTransform::fromTargetPoints (g1.x, g1.y, g1.x, g1.y, g2.x, g2.y, g2.x, g2.y, g3Source.x, g3Source.y, g3.x, g3.y); } if (g.point1 != g1 || g.point2 != g2 || fill.transform != t) { g.point1 = g1; g.point2 = g2; fill.transform = t; return true; } } return false; } bool DrawableShape::RelativeFillType::isDynamic() const { return gradientPoint1.isDynamic() || gradientPoint2.isDynamic() || gradientPoint3.isDynamic(); } void DrawableShape::RelativeFillType::writeTo (ValueTree& v, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const { if (fill.isColour()) { v.setProperty (FillAndStrokeState::type, "solid", undoManager); v.setProperty (FillAndStrokeState::colour, String::toHexString ((int) fill.colour.getARGB()), undoManager); } else if (fill.isGradient()) { v.setProperty (FillAndStrokeState::type, "gradient", undoManager); v.setProperty (FillAndStrokeState::gradientPoint1, gradientPoint1.toString(), undoManager); v.setProperty (FillAndStrokeState::gradientPoint2, gradientPoint2.toString(), undoManager); v.setProperty (FillAndStrokeState::gradientPoint3, gradientPoint3.toString(), undoManager); const ColourGradient& cg = *fill.gradient; v.setProperty (FillAndStrokeState::radial, cg.isRadial, undoManager); String s; for (int i = 0; i < cg.getNumColours(); ++i) s << ' ' << cg.getColourPosition (i) << ' ' << String::toHexString ((int) cg.getColour(i).getARGB()); v.setProperty (FillAndStrokeState::colours, s.trimStart(), undoManager); } else if (fill.isTiledImage()) { v.setProperty (FillAndStrokeState::type, "image", undoManager); if (imageProvider != nullptr) v.setProperty (FillAndStrokeState::imageId, imageProvider->getIdentifierForImage (fill.image), undoManager); if (fill.getOpacity() < 1.0f) v.setProperty (FillAndStrokeState::imageOpacity, fill.getOpacity(), undoManager); else v.removeProperty (FillAndStrokeState::imageOpacity, undoManager); } else { jassertfalse; } } bool DrawableShape::RelativeFillType::readFrom (const ValueTree& v, ComponentBuilder::ImageProvider* imageProvider) { const String newType (v [FillAndStrokeState::type].toString()); if (newType == "solid") { const String colourString (v [FillAndStrokeState::colour].toString()); fill.setColour (colourString.isEmpty() ? Colours::black : Colour::fromString (colourString)); return true; } else if (newType == "gradient") { ColourGradient g; g.isRadial = v [FillAndStrokeState::radial]; StringArray colourSteps; colourSteps.addTokens (v [FillAndStrokeState::colours].toString(), false); for (int i = 0; i < colourSteps.size() / 2; ++i) g.addColour (colourSteps[i * 2].getDoubleValue(), Colour::fromString (colourSteps[i * 2 + 1])); fill.setGradient (g); gradientPoint1 = RelativePoint (v [FillAndStrokeState::gradientPoint1]); gradientPoint2 = RelativePoint (v [FillAndStrokeState::gradientPoint2]); gradientPoint3 = RelativePoint (v [FillAndStrokeState::gradientPoint3]); return true; } else if (newType == "image") { Image im; if (imageProvider != nullptr) im = imageProvider->getImageForIdentifier (v [FillAndStrokeState::imageId]); fill.setTiledImage (im, AffineTransform::identity); fill.setOpacity ((float) v.getProperty (FillAndStrokeState::imageOpacity, 1.0f)); return true; } jassertfalse; return false; } //============================================================================== const Identifier DrawableShape::FillAndStrokeState::type ("type"); const Identifier DrawableShape::FillAndStrokeState::colour ("colour"); const Identifier DrawableShape::FillAndStrokeState::colours ("colours"); const Identifier DrawableShape::FillAndStrokeState::fill ("Fill"); const Identifier DrawableShape::FillAndStrokeState::stroke ("Stroke"); const Identifier DrawableShape::FillAndStrokeState::path ("Path"); const Identifier DrawableShape::FillAndStrokeState::jointStyle ("jointStyle"); const Identifier DrawableShape::FillAndStrokeState::capStyle ("capStyle"); const Identifier DrawableShape::FillAndStrokeState::strokeWidth ("strokeWidth"); const Identifier DrawableShape::FillAndStrokeState::gradientPoint1 ("point1"); const Identifier DrawableShape::FillAndStrokeState::gradientPoint2 ("point2"); const Identifier DrawableShape::FillAndStrokeState::gradientPoint3 ("point3"); const Identifier DrawableShape::FillAndStrokeState::radial ("radial"); const Identifier DrawableShape::FillAndStrokeState::imageId ("imageId"); const Identifier DrawableShape::FillAndStrokeState::imageOpacity ("imageOpacity"); DrawableShape::FillAndStrokeState::FillAndStrokeState (const ValueTree& state_) : Drawable::ValueTreeWrapperBase (state_) { } DrawableShape::RelativeFillType DrawableShape::FillAndStrokeState::getFill (const Identifier& fillOrStrokeType, ComponentBuilder::ImageProvider* imageProvider) const { DrawableShape::RelativeFillType f; f.readFrom (state.getChildWithName (fillOrStrokeType), imageProvider); return f; } ValueTree DrawableShape::FillAndStrokeState::getFillState (const Identifier& fillOrStrokeType) { ValueTree v (state.getChildWithName (fillOrStrokeType)); if (v.isValid()) return v; setFill (fillOrStrokeType, FillType (Colours::black), nullptr, nullptr); return getFillState (fillOrStrokeType); } void DrawableShape::FillAndStrokeState::setFill (const Identifier& fillOrStrokeType, const RelativeFillType& newFill, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) { ValueTree v (state.getOrCreateChildWithName (fillOrStrokeType, undoManager)); newFill.writeTo (v, imageProvider, undoManager); } PathStrokeType DrawableShape::FillAndStrokeState::getStrokeType() const { const String jointStyleString (state [jointStyle].toString()); const String capStyleString (state [capStyle].toString()); return PathStrokeType (state [strokeWidth], jointStyleString == "curved" ? PathStrokeType::curved : (jointStyleString == "bevel" ? PathStrokeType::beveled : PathStrokeType::mitered), capStyleString == "square" ? PathStrokeType::square : (capStyleString == "round" ? PathStrokeType::rounded : PathStrokeType::butt)); } void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager) { state.setProperty (strokeWidth, (double) newStrokeType.getStrokeThickness(), undoManager); state.setProperty (jointStyle, newStrokeType.getJointStyle() == PathStrokeType::mitered ? "miter" : (newStrokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), undoManager); state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); } static bool replaceColourInFill (DrawableShape::RelativeFillType& fill, Colour original, Colour replacement) { if (fill.fill.colour == original && fill.fill.isColour()) { fill = FillType (replacement); return true; } return false; } bool DrawableShape::replaceColour (Colour original, Colour replacement) { bool changed1 = replaceColourInFill (mainFill, original, replacement); bool changed2 = replaceColourInFill (strokeFill, original, replacement); return changed1 || changed2; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.h000066400000000000000000000160521320201440200325530ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLESHAPE_H_INCLUDED #define JUCE_DRAWABLESHAPE_H_INCLUDED //============================================================================== /** A base class implementing common functionality for Drawable classes which consist of some kind of filled and stroked outline. @see DrawablePath, DrawableRectangle */ class JUCE_API DrawableShape : public Drawable { protected: //============================================================================== DrawableShape(); DrawableShape (const DrawableShape&); public: /** Destructor. */ ~DrawableShape(); //============================================================================== /** A FillType wrapper that allows the gradient coordinates to be implemented using RelativePoint. */ class RelativeFillType { public: RelativeFillType(); RelativeFillType (const FillType& fill); RelativeFillType (const RelativeFillType&); RelativeFillType& operator= (const RelativeFillType&); bool operator== (const RelativeFillType&) const; bool operator!= (const RelativeFillType&) const; bool isDynamic() const; bool recalculateCoords (Expression::Scope* scope); void writeTo (ValueTree& v, ComponentBuilder::ImageProvider*, UndoManager*) const; bool readFrom (const ValueTree& v, ComponentBuilder::ImageProvider*); //============================================================================== FillType fill; RelativePoint gradientPoint1, gradientPoint2, gradientPoint3; }; //============================================================================== /** Sets a fill type for the path. This colour is used to fill the path - if you don't want the path to be filled (e.g. if you're just drawing an outline), set this to a transparent colour. @see setPath, setStrokeFill */ void setFill (const FillType& newFill); /** Sets a fill type for the path. This colour is used to fill the path - if you don't want the path to be filled (e.g. if you're just drawing an outline), set this to a transparent colour. @see setPath, setStrokeFill */ void setFill (const RelativeFillType& newFill); /** Returns the current fill type. @see setFill */ const RelativeFillType& getFill() const noexcept { return mainFill; } /** Sets the fill type with which the outline will be drawn. @see setFill */ void setStrokeFill (const FillType& newStrokeFill); /** Sets the fill type with which the outline will be drawn. @see setFill */ void setStrokeFill (const RelativeFillType& newStrokeFill); /** Returns the current stroke fill. @see setStrokeFill */ const RelativeFillType& getStrokeFill() const noexcept { return strokeFill; } /** Changes the properties of the outline that will be drawn around the path. If the stroke has 0 thickness, no stroke will be drawn. @see setStrokeThickness, setStrokeColour */ void setStrokeType (const PathStrokeType& newStrokeType); /** Changes the stroke thickness. This is a shortcut for calling setStrokeType. */ void setStrokeThickness (float newThickness); /** Returns the current outline style. */ const PathStrokeType& getStrokeType() const noexcept { return strokeType; } //============================================================================== /** @internal */ class FillAndStrokeState : public Drawable::ValueTreeWrapperBase { public: FillAndStrokeState (const ValueTree& state); ValueTree getFillState (const Identifier& fillOrStrokeType); RelativeFillType getFill (const Identifier& fillOrStrokeType, ComponentBuilder::ImageProvider*) const; void setFill (const Identifier& fillOrStrokeType, const RelativeFillType& newFill, ComponentBuilder::ImageProvider*, UndoManager*); PathStrokeType getStrokeType() const; void setStrokeType (const PathStrokeType& newStrokeType, UndoManager*); static const Identifier type, colour, colours, fill, stroke, path, jointStyle, capStyle, strokeWidth, gradientPoint1, gradientPoint2, gradientPoint3, radial, imageId, imageOpacity; }; /** @internal */ Rectangle getDrawableBounds() const override; /** @internal */ void paint (Graphics&) override; /** @internal */ bool hitTest (int x, int y) override; /** @internal */ bool replaceColour (Colour originalColour, Colour replacementColour) override; protected: //============================================================================== /** Called when the cached path should be updated. */ void pathChanged(); /** Called when the cached stroke should be updated. */ void strokeChanged(); /** True if there's a stroke with a non-zero thickness and non-transparent colour. */ bool isStrokeVisible() const noexcept; /** Updates the details from a FillAndStrokeState object, returning true if something changed. */ void refreshFillTypes (const FillAndStrokeState& newState, ComponentBuilder::ImageProvider*); /** Writes the stroke and fill details to a FillAndStrokeState object. */ void writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider*, UndoManager*) const; //============================================================================== PathStrokeType strokeType; Path path, strokePath; private: class RelativePositioner; RelativeFillType mainFill, strokeFill; ScopedPointer mainFillPositioner, strokeFillPositioner; void setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill, ScopedPointer& positioner); DrawableShape& operator= (const DrawableShape&); }; #endif // JUCE_DRAWABLESHAPE_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp000066400000000000000000000252011320201440200327660ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DrawableText::DrawableText() : colour (Colours::black), justification (Justification::centredLeft) { setBoundingBox (RelativeParallelogram (RelativePoint (0.0f, 0.0f), RelativePoint (50.0f, 0.0f), RelativePoint (0.0f, 20.0f))); setFont (Font (15.0f), true); } DrawableText::DrawableText (const DrawableText& other) : Drawable (other), bounds (other.bounds), fontHeight (other.fontHeight), fontHScale (other.fontHScale), font (other.font), text (other.text), colour (other.colour), justification (other.justification) { refreshBounds(); } DrawableText::~DrawableText() { } //============================================================================== void DrawableText::setText (const String& newText) { if (text != newText) { text = newText; refreshBounds(); } } void DrawableText::setColour (Colour newColour) { if (colour != newColour) { colour = newColour; repaint(); } } void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) { if (font != newFont) { font = newFont; if (applySizeAndScale) { fontHeight = font.getHeight(); fontHScale = font.getHorizontalScale(); } refreshBounds(); } } void DrawableText::setJustification (Justification newJustification) { justification = newJustification; repaint(); } void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds) { if (bounds != newBounds) { bounds = newBounds; refreshBounds(); } } void DrawableText::setFontHeight (const RelativeCoordinate& newHeight) { if (fontHeight != newHeight) { fontHeight = newHeight; refreshBounds(); } } void DrawableText::setFontHorizontalScale (const RelativeCoordinate& newScale) { if (fontHScale != newScale) { fontHScale = newScale; refreshBounds(); } } void DrawableText::refreshBounds() { if (bounds.isDynamic() || fontHeight.isDynamic() || fontHScale.isDynamic()) { Drawable::Positioner* const p = new Drawable::Positioner (*this); setPositioner (p); p->apply(); } else { setPositioner (0); recalculateCoordinates (0); } } bool DrawableText::registerCoordinates (RelativeCoordinatePositionerBase& pos) { bool ok = pos.addPoint (bounds.topLeft); ok = pos.addPoint (bounds.topRight) && ok; ok = pos.addPoint (bounds.bottomLeft) && ok; ok = pos.addCoordinate (fontHeight) && ok; return pos.addCoordinate (fontHScale) && ok; } void DrawableText::recalculateCoordinates (Expression::Scope* scope) { bounds.resolveThreePoints (resolvedPoints, scope); const float w = Line (resolvedPoints[0], resolvedPoints[1]).getLength(); const float h = Line (resolvedPoints[0], resolvedPoints[2]).getLength(); const float height = jlimit (0.01f, jmax (0.01f, h), (float) fontHeight.resolve (scope)); const float hscale = jlimit (0.01f, jmax (0.01f, w), (float) fontHScale.resolve (scope)); scaledFont = font; scaledFont.setHeight (height); scaledFont.setHorizontalScale (hscale); setBoundsToEnclose (getDrawableBounds()); repaint(); } //============================================================================== void DrawableText::paint (Graphics& g) { transformContextToCorrectOrigin (g); const float w = Line (resolvedPoints[0], resolvedPoints[1]).getLength(); const float h = Line (resolvedPoints[0], resolvedPoints[2]).getLength(); g.addTransform (AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y, w, 0, resolvedPoints[1].x, resolvedPoints[1].y, 0, h, resolvedPoints[2].x, resolvedPoints[2].y)); g.setFont (scaledFont); g.setColour (colour); g.drawFittedText (text, Rectangle (w, h).getSmallestIntegerContainer(), justification, 0x100000); } Rectangle DrawableText::getDrawableBounds() const { return RelativeParallelogram::getBoundingBox (resolvedPoints); } Drawable* DrawableText::createCopy() const { return new DrawableText (*this); } //============================================================================== const Identifier DrawableText::valueTreeType ("Text"); const Identifier DrawableText::ValueTreeWrapper::text ("text"); const Identifier DrawableText::ValueTreeWrapper::colour ("colour"); const Identifier DrawableText::ValueTreeWrapper::font ("font"); const Identifier DrawableText::ValueTreeWrapper::justification ("justification"); const Identifier DrawableText::ValueTreeWrapper::topLeft ("topLeft"); const Identifier DrawableText::ValueTreeWrapper::topRight ("topRight"); const Identifier DrawableText::ValueTreeWrapper::bottomLeft ("bottomLeft"); const Identifier DrawableText::ValueTreeWrapper::fontHeight ("fontHeight"); const Identifier DrawableText::ValueTreeWrapper::fontHScale ("fontHScale"); //============================================================================== DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) : ValueTreeWrapperBase (state_) { jassert (state.hasType (valueTreeType)); } String DrawableText::ValueTreeWrapper::getText() const { return state [text].toString(); } void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager) { state.setProperty (text, newText, undoManager); } Value DrawableText::ValueTreeWrapper::getTextValue (UndoManager* undoManager) { return state.getPropertyAsValue (text, undoManager); } Colour DrawableText::ValueTreeWrapper::getColour() const { return Colour::fromString (state [colour].toString()); } void DrawableText::ValueTreeWrapper::setColour (Colour newColour, UndoManager* undoManager) { state.setProperty (colour, newColour.toString(), undoManager); } Justification DrawableText::ValueTreeWrapper::getJustification() const { return Justification ((int) state [justification]); } void DrawableText::ValueTreeWrapper::setJustification (Justification newJustification, UndoManager* undoManager) { state.setProperty (justification, newJustification.getFlags(), undoManager); } Font DrawableText::ValueTreeWrapper::getFont() const { return Font::fromString (state [font]); } void DrawableText::ValueTreeWrapper::setFont (const Font& newFont, UndoManager* undoManager) { state.setProperty (font, newFont.toString(), undoManager); } Value DrawableText::ValueTreeWrapper::getFontValue (UndoManager* undoManager) { return state.getPropertyAsValue (font, undoManager); } RelativeParallelogram DrawableText::ValueTreeWrapper::getBoundingBox() const { return RelativeParallelogram (state [topLeft].toString(), state [topRight].toString(), state [bottomLeft].toString()); } void DrawableText::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager) { state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager); state.setProperty (topRight, newBounds.topRight.toString(), undoManager); state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager); } RelativeCoordinate DrawableText::ValueTreeWrapper::getFontHeight() const { return state [fontHeight].toString(); } void DrawableText::ValueTreeWrapper::setFontHeight (const RelativeCoordinate& coord, UndoManager* undoManager) { state.setProperty (fontHeight, coord.toString(), undoManager); } RelativeCoordinate DrawableText::ValueTreeWrapper::getFontHorizontalScale() const { return state [fontHScale].toString(); } void DrawableText::ValueTreeWrapper::setFontHorizontalScale (const RelativeCoordinate& coord, UndoManager* undoManager) { state.setProperty (fontHScale, coord.toString(), undoManager); } //============================================================================== void DrawableText::refreshFromValueTree (const ValueTree& tree, ComponentBuilder&) { ValueTreeWrapper v (tree); setComponentID (v.getID()); const RelativeParallelogram newBounds (v.getBoundingBox()); const RelativeCoordinate newFontHeight (v.getFontHeight()); const RelativeCoordinate newFontHScale (v.getFontHorizontalScale()); const Colour newColour (v.getColour()); const Justification newJustification (v.getJustification()); const String newText (v.getText()); const Font newFont (v.getFont()); if (text != newText || font != newFont || justification != newJustification || colour != newColour || bounds != newBounds || newFontHeight != fontHeight || newFontHScale != fontHScale) { setBoundingBox (newBounds); setFontHeight (newFontHeight); setFontHorizontalScale (newFontHScale); setColour (newColour); setFont (newFont, false); setJustification (newJustification); setText (newText); } } ValueTree DrawableText::createValueTree (ComponentBuilder::ImageProvider*) const { ValueTree tree (valueTreeType); ValueTreeWrapper v (tree); v.setID (getComponentID()); v.setText (text, nullptr); v.setFont (font, nullptr); v.setJustification (justification, nullptr); v.setColour (colour, nullptr); v.setBoundingBox (bounds, nullptr); v.setFontHeight (fontHeight, nullptr); v.setFontHorizontalScale (fontHScale, nullptr); return tree; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h000066400000000000000000000141531320201440200324370ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DRAWABLETEXT_H_INCLUDED #define JUCE_DRAWABLETEXT_H_INCLUDED //============================================================================== /** A drawable object which renders a line of text. @see Drawable */ class JUCE_API DrawableText : public Drawable { public: //============================================================================== /** Creates a DrawableText object. */ DrawableText(); DrawableText (const DrawableText&); /** Destructor. */ ~DrawableText(); //============================================================================== /** Sets the text to display.*/ void setText (const String& newText); /** Returns the currently displayed text */ const String& getText() const noexcept { return text;} /** Sets the colour of the text. */ void setColour (Colour newColour); /** Returns the current text colour. */ Colour getColour() const noexcept { return colour; } /** Sets the font to use. Note that the font height and horizontal scale are set as RelativeCoordinates using setFontHeight and setFontHorizontalScale. If applySizeAndScale is true, then these height and scale values will be changed to match the dimensions of the font supplied; if it is false, then the new font object's height and scale are ignored. */ void setFont (const Font& newFont, bool applySizeAndScale); /** Returns the current font. */ const Font& getFont() const noexcept { return font; } /** Changes the justification of the text within the bounding box. */ void setJustification (Justification newJustification); /** Returns the current justification. */ Justification getJustification() const noexcept { return justification; } /** Returns the parallelogram that defines the text bounding box. */ const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; } /** Sets the bounding box that contains the text. */ void setBoundingBox (const RelativeParallelogram& newBounds); const RelativeCoordinate& getFontHeight() const { return fontHeight; } void setFontHeight (const RelativeCoordinate& newHeight); const RelativeCoordinate& getFontHorizontalScale() const { return fontHScale; } void setFontHorizontalScale (const RelativeCoordinate& newScale); //============================================================================== /** @internal */ void paint (Graphics&) override; /** @internal */ Drawable* createCopy() const override; /** @internal */ void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder); /** @internal */ ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const override; /** @internal */ static const Identifier valueTreeType; /** @internal */ Rectangle getDrawableBounds() const override; //============================================================================== /** Internally-used class for wrapping a DrawableText's state into a ValueTree. */ class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase { public: ValueTreeWrapper (const ValueTree& state); String getText() const; void setText (const String& newText, UndoManager* undoManager); Value getTextValue (UndoManager* undoManager); Colour getColour() const; void setColour (Colour newColour, UndoManager* undoManager); Justification getJustification() const; void setJustification (Justification newJustification, UndoManager* undoManager); Font getFont() const; void setFont (const Font& newFont, UndoManager* undoManager); Value getFontValue (UndoManager* undoManager); RelativeParallelogram getBoundingBox() const; void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager); RelativeCoordinate getFontHeight() const; void setFontHeight (const RelativeCoordinate& newHeight, UndoManager* undoManager); RelativeCoordinate getFontHorizontalScale() const; void setFontHorizontalScale (const RelativeCoordinate& newScale, UndoManager* undoManager); static const Identifier text, colour, font, justification, topLeft, topRight, bottomLeft, fontHeight, fontHScale; }; private: //============================================================================== RelativeParallelogram bounds; RelativeCoordinate fontHeight, fontHScale; Point resolvedPoints[3]; Font font, scaledFont; String text; Colour colour; Justification justification; friend class Drawable::Positioner; bool registerCoordinates (RelativeCoordinatePositionerBase&); void recalculateCoordinates (Expression::Scope*); void refreshBounds(); DrawableText& operator= (const DrawableText&); JUCE_LEAK_DETECTOR (DrawableText) }; #endif // JUCE_DRAWABLETEXT_H_INCLUDED libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp000066400000000000000000001371531320201440200322260ustar00rootroot00000000000000/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class SVGState { public: //============================================================================== explicit SVGState (const XmlElement* const topLevel) : topLevelXml (topLevel, nullptr), elementX (0), elementY (0), width (512), height (512), viewBoxW (0), viewBoxH (0) { } struct XmlPath { XmlPath (const XmlElement* e, const XmlPath* p) noexcept : xml (e), parent (p) {} const XmlElement& operator*() const noexcept { jassert (xml != nullptr); return *xml; } const XmlElement* operator->() const noexcept { return xml; } XmlPath getChild (const XmlElement* e) const noexcept { return XmlPath (e, this); } const XmlElement* xml; const XmlPath* parent; }; //============================================================================== Drawable* parseSVGElement (const XmlPath& xml) { if (! xml->hasTagNameIgnoringNamespace ("svg")) return nullptr; DrawableComposite* const drawable = new DrawableComposite(); setCommonAttributes (*drawable, xml); SVGState newState (*this); if (xml->hasAttribute ("transform")) newState.addTransform (xml); newState.elementX = getCoordLength (xml->getStringAttribute ("x", String (newState.elementX)), viewBoxW); newState.elementY = getCoordLength (xml->getStringAttribute ("y", String (newState.elementY)), viewBoxH); newState.width = getCoordLength (xml->getStringAttribute ("width", String (newState.width)), viewBoxW); newState.height = getCoordLength (xml->getStringAttribute ("height", String (newState.height)), viewBoxH); if (newState.width <= 0) newState.width = 100; if (newState.height <= 0) newState.height = 100; Point viewboxXY; if (xml->hasAttribute ("viewBox")) { const String viewBoxAtt (xml->getStringAttribute ("viewBox")); String::CharPointerType viewParams (viewBoxAtt.getCharPointer()); Point vwh; if (parseCoords (viewParams, viewboxXY, true) && parseCoords (viewParams, vwh, true) && vwh.x > 0 && vwh.y > 0) { newState.viewBoxW = vwh.x; newState.viewBoxH = vwh.y; const int placementFlags = parsePlacementFlags (xml->getStringAttribute ("preserveAspectRatio").trim()); if (placementFlags != 0) newState.transform = RectanglePlacement (placementFlags) .getTransformToFit (Rectangle (viewboxXY.x, viewboxXY.y, vwh.x, vwh.y), Rectangle (newState.width, newState.height)) .followedBy (newState.transform); } } else { if (viewBoxW == 0) newState.viewBoxW = newState.width; if (viewBoxH == 0) newState.viewBoxH = newState.height; } newState.parseSubElements (xml, *drawable); drawable->setContentArea (RelativeRectangle (RelativeCoordinate (viewboxXY.x), RelativeCoordinate (viewboxXY.x + newState.viewBoxW), RelativeCoordinate (viewboxXY.y), RelativeCoordinate (viewboxXY.y + newState.viewBoxH))); drawable->resetBoundingBoxToContentArea(); return drawable; } //============================================================================== void parsePathString (Path& path, const String& pathString) const { String::CharPointerType d (pathString.getCharPointer().findEndOfWhitespace()); Point subpathStart, last, last2, p1, p2, p3; juce_wchar lastCommandChar = 0; bool isRelative = true; bool carryOn = true; const CharPointer_ASCII validCommandChars ("MmLlHhVvCcSsQqTtAaZz"); while (! d.isEmpty()) { if (validCommandChars.indexOf (*d) >= 0) { lastCommandChar = d.getAndAdvance(); isRelative = (lastCommandChar >= 'a' && lastCommandChar <= 'z'); } switch (lastCommandChar) { case 'M': case 'm': case 'L': case 'l': if (parseCoordsOrSkip (d, p1, false)) { if (isRelative) p1 += last; if (lastCommandChar == 'M' || lastCommandChar == 'm') { subpathStart = p1; path.startNewSubPath (p1); lastCommandChar = 'l'; } else path.lineTo (p1); last2 = last; last = p1; } break; case 'H': case 'h': if (parseCoord (d, p1.x, false, true)) { if (isRelative) p1.x += last.x; path.lineTo (p1.x, last.y); last2.x = last.x; last.x = p1.x; } else { ++d; } break; case 'V': case 'v': if (parseCoord (d, p1.y, false, false)) { if (isRelative) p1.y += last.y; path.lineTo (last.x, p1.y); last2.y = last.y; last.y = p1.y; } else { ++d; } break; case 'C': case 'c': if (parseCoordsOrSkip (d, p1, false) && parseCoordsOrSkip (d, p2, false) && parseCoordsOrSkip (d, p3, false)) { if (isRelative) { p1 += last; p2 += last; p3 += last; } path.cubicTo (p1, p2, p3); last2 = p2; last = p3; } break; case 'S': case 's': if (parseCoordsOrSkip (d, p1, false) && parseCoordsOrSkip (d, p3, false)) { if (isRelative) { p1 += last; p3 += last; } p2 = last + (last - last2); path.cubicTo (p2, p1, p3); last2 = p1; last = p3; } break; case 'Q': case 'q': if (parseCoordsOrSkip (d, p1, false) && parseCoordsOrSkip (d, p2, false)) { if (isRelative) { p1 += last; p2 += last; } path.quadraticTo (p1, p2); last2 = p1; last = p2; } break; case 'T': case 't': if (parseCoordsOrSkip (d, p1, false)) { if (isRelative) p1 += last; p2 = last + (last - last2); path.quadraticTo (p2, p1); last2 = p2; last = p1; } break; case 'A': case 'a': if (parseCoordsOrSkip (d, p1, false)) { String num; if (parseNextNumber (d, num, false)) { const float angle = degreesToRadians (num.getFloatValue()); if (parseNextNumber (d, num, false)) { const bool largeArc = num.getIntValue() != 0; if (parseNextNumber (d, num, false)) { const bool sweep = num.getIntValue() != 0; if (parseCoordsOrSkip (d, p2, false)) { if (isRelative) p2 += last; if (last != p2) { double centreX, centreY, startAngle, deltaAngle; double rx = p1.x, ry = p1.y; endpointToCentreParameters (last.x, last.y, p2.x, p2.y, angle, largeArc, sweep, rx, ry, centreX, centreY, startAngle, deltaAngle); path.addCentredArc ((float) centreX, (float) centreY, (float) rx, (float) ry, angle, (float) startAngle, (float) (startAngle + deltaAngle), false); path.lineTo (p2); } last2 = last; last = p2; } } } } } break; case 'Z': case 'z': path.closeSubPath(); last = last2 = subpathStart; d = d.findEndOfWhitespace(); lastCommandChar = 'M'; break; default: carryOn = false; break; } if (! carryOn) break; } // paths that finish back at their start position often seem to be // left without a 'z', so need to be closed explicitly.. if (path.getCurrentPosition() == subpathStart) path.closeSubPath(); } private: //============================================================================== const XmlPath topLevelXml; float elementX, elementY, width, height, viewBoxW, viewBoxH; AffineTransform transform; String cssStyleText; static void setCommonAttributes (Drawable& d, const XmlPath& xml) { String compID (xml->getStringAttribute ("id")); d.setName (compID); d.setComponentID (compID); if (xml->getStringAttribute ("display") == "none") d.setVisible (false); } //============================================================================== void parseSubElements (const XmlPath& xml, DrawableComposite& parentDrawable) { forEachXmlChildElement (*xml, e) parentDrawable.addAndMakeVisible (parseSubElement (xml.getChild (e))); } Drawable* parseSubElement (const XmlPath& xml) { const String tag (xml->getTagNameWithoutNamespace()); if (tag == "g") return parseGroupElement (xml); if (tag == "svg") return parseSVGElement (xml); if (tag == "path") return parsePath (xml); if (tag == "rect") return parseRect (xml); if (tag == "circle") return parseCircle (xml); if (tag == "ellipse") return parseEllipse (xml); if (tag == "line") return parseLine (xml); if (tag == "polyline") return parsePolygon (xml, true); if (tag == "polygon") return parsePolygon (xml, false); if (tag == "text") return parseText (xml, true); if (tag == "switch") return parseSwitch (xml); if (tag == "style") parseCSSStyle (xml); return nullptr; } DrawableComposite* parseSwitch (const XmlPath& xml) { if (const XmlElement* const group = xml->getChildByName ("g")) return parseGroupElement (xml.getChild (group)); return nullptr; } DrawableComposite* parseGroupElement (const XmlPath& xml) { DrawableComposite* const drawable = new DrawableComposite(); setCommonAttributes (*drawable, xml); if (xml->hasAttribute ("transform")) { SVGState newState (*this); newState.addTransform (xml); newState.parseSubElements (xml, *drawable); } else { parseSubElements (xml, *drawable); } drawable->resetContentAreaAndBoundingBoxToFitChildren(); return drawable; } //============================================================================== Drawable* parsePath (const XmlPath& xml) const { Path path; parsePathString (path, xml->getStringAttribute ("d")); if (getStyleAttribute (xml, "fill-rule").trim().equalsIgnoreCase ("evenodd")) path.setUsingNonZeroWinding (false); return parseShape (xml, path); } Drawable* parseRect (const XmlPath& xml) const { Path rect; const bool hasRX = xml->hasAttribute ("rx"); const bool hasRY = xml->hasAttribute ("ry"); if (hasRX || hasRY) { float rx = getCoordLength (xml, "rx", viewBoxW); float ry = getCoordLength (xml, "ry", viewBoxH); if (! hasRX) rx = ry; else if (! hasRY) ry = rx; rect.addRoundedRectangle (getCoordLength (xml, "x", viewBoxW), getCoordLength (xml, "y", viewBoxH), getCoordLength (xml, "width", viewBoxW), getCoordLength (xml, "height", viewBoxH), rx, ry); } else { rect.addRectangle (getCoordLength (xml, "x", viewBoxW), getCoordLength (xml, "y", viewBoxH), getCoordLength (xml, "width", viewBoxW), getCoordLength (xml, "height", viewBoxH)); } return parseShape (xml, rect); } Drawable* parseCircle (const XmlPath& xml) const { Path circle; const float cx = getCoordLength (xml, "cx", viewBoxW); const float cy = getCoordLength (xml, "cy", viewBoxH); const float radius = getCoordLength (xml, "r", viewBoxW); circle.addEllipse (cx - radius, cy - radius, radius * 2.0f, radius * 2.0f); return parseShape (xml, circle); } Drawable* parseEllipse (const XmlPath& xml) const { Path ellipse; const float cx = getCoordLength (xml, "cx", viewBoxW); const float cy = getCoordLength (xml, "cy", viewBoxH); const float radiusX = getCoordLength (xml, "rx", viewBoxW); const float radiusY = getCoordLength (xml, "ry", viewBoxH); ellipse.addEllipse (cx - radiusX, cy - radiusY, radiusX * 2.0f, radiusY * 2.0f); return parseShape (xml, ellipse); } Drawable* parseLine (const XmlPath& xml) const { Path line; const float x1 = getCoordLength (xml, "x1", viewBoxW); const float y1 = getCoordLength (xml, "y1", viewBoxH); const float x2 = getCoordLength (xml, "x2", viewBoxW); const float y2 = getCoordLength (xml, "y2", viewBoxH); line.startNewSubPath (x1, y1); line.lineTo (x2, y2); return parseShape (xml, line); } Drawable* parsePolygon (const XmlPath& xml, const bool isPolyline) const { const String pointsAtt (xml->getStringAttribute ("points")); String::CharPointerType points (pointsAtt.getCharPointer()); Path path; Point p; if (parseCoords (points, p, true)) { Point first (p), last; path.startNewSubPath (first); while (parseCoords (points, p, true)) { last = p; path.lineTo (p); } if ((! isPolyline) || first == last) path.closeSubPath(); } return parseShape (xml, path); } //============================================================================== Drawable* parseShape (const XmlPath& xml, Path& path, const bool shouldParseTransform = true) const { if (shouldParseTransform && xml->hasAttribute ("transform")) { SVGState newState (*this); newState.addTransform (xml); return newState.parseShape (xml, path, false); } DrawablePath* dp = new DrawablePath(); setCommonAttributes (*dp, xml); dp->setFill (Colours::transparentBlack); path.applyTransform (transform); dp->setPath (path); dp->setFill (getPathFillType (path, getStyleAttribute (xml, "fill"), getStyleAttribute (xml, "fill-opacity"), getStyleAttribute (xml, "opacity"), pathContainsClosedSubPath (path) ? Colours::black : Colours::transparentBlack)); const String strokeType (getStyleAttribute (xml, "stroke")); if (strokeType.isNotEmpty() && ! strokeType.equalsIgnoreCase ("none")) { dp->setStrokeFill (getPathFillType (path, strokeType, getStyleAttribute (xml, "stroke-opacity"), getStyleAttribute (xml, "opacity"), Colours::transparentBlack)); dp->setStrokeType (getStrokeFor (xml)); } return dp; } static bool pathContainsClosedSubPath (const Path& path) noexcept { for (Path::Iterator iter (path); iter.next();) if (iter.elementType == Path::Iterator::closePath) return true; return false; } struct SetGradientStopsOp { const SVGState* state; ColourGradient* gradient; void operator() (const XmlPath& xml) { state->addGradientStopsIn (*gradient, xml); } }; void addGradientStopsIn (ColourGradient& cg, const XmlPath& fillXml) const { if (fillXml.xml != nullptr) { forEachXmlChildElementWithTagName (*fillXml, e, "stop") { int index = 0; Colour col (parseColour (getStyleAttribute (fillXml.getChild (e), "stop-color"), index, Colours::black)); const String opacity (getStyleAttribute (fillXml.getChild (e), "stop-opacity", "1")); col = col.withMultipliedAlpha (jlimit (0.0f, 1.0f, opacity.getFloatValue())); double offset = e->getDoubleAttribute ("offset"); if (e->getStringAttribute ("offset").containsChar ('%')) offset *= 0.01; cg.addColour (jlimit (0.0, 1.0, offset), col); } } } FillType getGradientFillType (const XmlPath& fillXml, const Path& path, const float opacity) const { ColourGradient gradient; { const String id (fillXml->getStringAttribute ("xlink:href")); if (id.startsWithChar ('#')) { SetGradientStopsOp op = { this, &gradient, }; findElementForId (topLevelXml, id.substring (1), op); } } addGradientStopsIn (gradient, fillXml); if (gradient.getNumColours() > 0) { gradient.addColour (0.0, gradient.getColour (0)); gradient.addColour (1.0, gradient.getColour (gradient.getNumColours() - 1)); } else { gradient.addColour (0.0, Colours::black); gradient.addColour (1.0, Colours::black); } if (opacity < 1.0f) gradient.multiplyOpacity (opacity); jassert (gradient.getNumColours() > 0); gradient.isRadial = fillXml->hasTagNameIgnoringNamespace ("radialGradient"); float gradientWidth = viewBoxW; float gradientHeight = viewBoxH; float dx = 0.0f; float dy = 0.0f; const bool userSpace = fillXml->getStringAttribute ("gradientUnits").equalsIgnoreCase ("userSpaceOnUse"); if (! userSpace) { const Rectangle bounds (path.getBounds()); dx = bounds.getX(); dy = bounds.getY(); gradientWidth = bounds.getWidth(); gradientHeight = bounds.getHeight(); } if (gradient.isRadial) { if (userSpace) gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("cx", "50%"), gradientWidth), dy + getCoordLength (fillXml->getStringAttribute ("cy", "50%"), gradientHeight)); else gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("cx", "50%"), 1.0f), dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("cy", "50%"), 1.0f)); const float radius = getCoordLength (fillXml->getStringAttribute ("r", "50%"), gradientWidth); gradient.point2 = gradient.point1 + Point (radius, 0.0f); //xxx (the fx, fy focal point isn't handled properly here..) } else { if (userSpace) { gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x1", "0%"), gradientWidth), dy + getCoordLength (fillXml->getStringAttribute ("y1", "0%"), gradientHeight)); gradient.point2.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x2", "100%"), gradientWidth), dy + getCoordLength (fillXml->getStringAttribute ("y2", "0%"), gradientHeight)); } else { gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x1", "0%"), 1.0f), dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y1", "0%"), 1.0f)); gradient.point2.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x2", "100%"), 1.0f), dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y2", "0%"), 1.0f)); } if (gradient.point1 == gradient.point2) return Colour (gradient.getColour (gradient.getNumColours() - 1)); } FillType type (gradient); const AffineTransform gradientTransform (parseTransform (fillXml->getStringAttribute ("gradientTransform")) .followedBy (transform)); if (gradient.isRadial) { type.transform = gradientTransform; } else { // Transform the perpendicular vector into the new coordinate space for the gradient. // This vector is now the slope of the linear gradient as it should appear in the new coord space const Point perpendicular (Point (gradient.point2.y - gradient.point1.y, gradient.point1.x - gradient.point2.x) .transformedBy (gradientTransform.withAbsoluteTranslation (0, 0))); const Point newGradPoint1 (gradient.point1.transformedBy (gradientTransform)); const Point newGradPoint2 (gradient.point2.transformedBy (gradientTransform)); // Project the transformed gradient vector onto the transformed slope of the linear // gradient as it should appear in the new coordinate space const float scale = perpendicular.getDotProduct (newGradPoint2 - newGradPoint1) / perpendicular.getDotProduct (perpendicular); type.gradient->point1 = newGradPoint1; type.gradient->point2 = newGradPoint2 - perpendicular * scale; } return type; } struct GetFillTypeOp { const SVGState* state; FillType* dest; const Path* path; float opacity; void operator() (const XmlPath& xml) { if (xml->hasTagNameIgnoringNamespace ("linearGradient") || xml->hasTagNameIgnoringNamespace ("radialGradient")) *dest = state->getGradientFillType (xml, *path, opacity); } }; FillType getPathFillType (const Path& path, const String& fill, const String& fillOpacity, const String& overallOpacity, const Colour defaultColour) const { float opacity = 1.0f; if (overallOpacity.isNotEmpty()) opacity = jlimit (0.0f, 1.0f, overallOpacity.getFloatValue()); if (fillOpacity.isNotEmpty()) opacity *= (jlimit (0.0f, 1.0f, fillOpacity.getFloatValue())); if (fill.startsWithIgnoreCase ("url")) { const String id (fill.fromFirstOccurrenceOf ("#", false, false) .upToLastOccurrenceOf (")", false, false).trim()); FillType result; GetFillTypeOp op = { this, &result, &path, opacity }; if (findElementForId (topLevelXml, id, op)) return result; } if (fill.equalsIgnoreCase ("none")) return Colours::transparentBlack; int i = 0; return parseColour (fill, i, defaultColour).withMultipliedAlpha (opacity); } static PathStrokeType::JointStyle getJointStyle (const String& join) noexcept { if (join.equalsIgnoreCase ("round")) return PathStrokeType::curved; if (join.equalsIgnoreCase ("bevel")) return PathStrokeType::beveled; return PathStrokeType::mitered; } static PathStrokeType::EndCapStyle getEndCapStyle (const String& cap) noexcept { if (cap.equalsIgnoreCase ("round")) return PathStrokeType::rounded; if (cap.equalsIgnoreCase ("square")) return PathStrokeType::square; return PathStrokeType::butt; } float getStrokeWidth (const String& strokeWidth) const noexcept { return transform.getScaleFactor() * getCoordLength (strokeWidth, viewBoxW); } PathStrokeType getStrokeFor (const XmlPath& xml) const { return PathStrokeType (getStrokeWidth (getStyleAttribute (xml, "stroke-width", "1")), getJointStyle (getStyleAttribute (xml, "stroke-linejoin")), getEndCapStyle (getStyleAttribute (xml, "stroke-linecap"))); } //============================================================================== Drawable* parseText (const XmlPath& xml, bool shouldParseTransform) { if (shouldParseTransform && xml->hasAttribute ("transform")) { SVGState newState (*this); newState.addTransform (xml); return newState.parseText (xml, false); } Array xCoords, yCoords, dxCoords, dyCoords; getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true); getCoordList (yCoords, getInheritedAttribute (xml, "y"), true, false); getCoordList (dxCoords, getInheritedAttribute (xml, "dx"), true, true); getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false); const Font font (getFont (xml)); const String anchorStr = getStyleAttribute(xml, "text-anchor"); DrawableComposite* dc = new DrawableComposite(); setCommonAttributes (*dc, xml); forEachXmlChildElement (*xml, e) { if (e->isTextElement()) { const String text (e->getText().trim()); DrawableText* dt = new DrawableText(); dc->addAndMakeVisible (dt); dt->setText (text); dt->setFont (font, true); dt->setTransform (transform); int i = 0; dt->setColour (parseColour (getStyleAttribute (xml, "fill"), i, Colours::black) .withMultipliedAlpha (getStyleAttribute (xml, "fill-opacity", "1").getFloatValue())); Rectangle bounds (xCoords[0], yCoords[0] - font.getAscent(), font.getStringWidthFloat (text), font.getHeight()); if (anchorStr == "middle") bounds.setX (bounds.getX() - bounds.getWidth() / 2.0f); else if (anchorStr == "end") bounds.setX (bounds.getX() - bounds.getWidth()); dt->setBoundingBox (bounds); } else if (e->hasTagNameIgnoringNamespace ("tspan")) { dc->addAndMakeVisible (parseText (xml.getChild (e), true)); } } return dc; } Font getFont (const XmlPath& xml) const { const float fontSize = getCoordLength (getStyleAttribute (xml, "font-size"), 1.0f); int style = getStyleAttribute (xml, "font-style").containsIgnoreCase ("italic") ? Font::italic : Font::plain; if (getStyleAttribute (xml, "font-weight").containsIgnoreCase ("bold")) style |= Font::bold; const String family (getStyleAttribute (xml, "font-family")); return family.isEmpty() ? Font (fontSize, style) : Font (family, fontSize, style); } //============================================================================== void addTransform (const XmlPath& xml) { transform = parseTransform (xml->getStringAttribute ("transform")) .followedBy (transform); } //============================================================================== bool parseCoord (String::CharPointerType& s, float& value, const bool allowUnits, const bool isX) const { String number; if (! parseNextNumber (s, number, allowUnits)) { value = 0; return false; } value = getCoordLength (number, isX ? viewBoxW : viewBoxH); return true; } bool parseCoords (String::CharPointerType& s, Point& p, const bool allowUnits) const { return parseCoord (s, p.x, allowUnits, true) && parseCoord (s, p.y, allowUnits, false); } bool parseCoordsOrSkip (String::CharPointerType& s, Point& p, const bool allowUnits) const { if (parseCoords (s, p, allowUnits)) return true; if (! s.isEmpty()) ++s; return false; } float getCoordLength (const String& s, const float sizeForProportions) const noexcept { float n = s.getFloatValue(); const int len = s.length(); if (len > 2) { const float dpi = 96.0f; const juce_wchar n1 = s [len - 2]; const juce_wchar n2 = s [len - 1]; if (n1 == 'i' && n2 == 'n') n *= dpi; else if (n1 == 'm' && n2 == 'm') n *= dpi / 25.4f; else if (n1 == 'c' && n2 == 'm') n *= dpi / 2.54f; else if (n1 == 'p' && n2 == 'c') n *= 15.0f; else if (n2 == '%') n *= 0.01f * sizeForProportions; } return n; } float getCoordLength (const XmlPath& xml, const char* attName, const float sizeForProportions) const noexcept { return getCoordLength (xml->getStringAttribute (attName), sizeForProportions); } void getCoordList (Array& coords, const String& list, bool allowUnits, const bool isX) const { String::CharPointerType text (list.getCharPointer()); float value; while (parseCoord (text, value, allowUnits, isX)) coords.add (value); } //============================================================================== void parseCSSStyle (const XmlPath& xml) { cssStyleText = xml->getAllSubText() + "\n" + cssStyleText; } static String::CharPointerType findStyleItem (String::CharPointerType source, String::CharPointerType name) { const int nameLength = (int) name.length(); while (! source.isEmpty()) { if (source.getAndAdvance() == '.' && CharacterFunctions::compareIgnoreCaseUpTo (source, name, nameLength) == 0) { String::CharPointerType endOfName ((source + nameLength).findEndOfWhitespace()); if (*endOfName == '{') return endOfName; } } return source; } String getStyleAttribute (const XmlPath& xml, StringRef attributeName, const String& defaultValue = String()) const { if (xml->hasAttribute (attributeName)) return xml->getStringAttribute (attributeName, defaultValue); const String styleAtt (xml->getStringAttribute ("style")); if (styleAtt.isNotEmpty()) { const String value (getAttributeFromStyleList (styleAtt, attributeName, String())); if (value.isNotEmpty()) return value; } else if (xml->hasAttribute ("class")) { String::CharPointerType openBrace = findStyleItem (cssStyleText.getCharPointer(), xml->getStringAttribute ("class").getCharPointer()); if (! openBrace.isEmpty()) { String::CharPointerType closeBrace = CharacterFunctions::find (openBrace, (juce_wchar) '}'); if (closeBrace != openBrace) { const String value (getAttributeFromStyleList (String (openBrace + 1, closeBrace), attributeName, defaultValue)); if (value.isNotEmpty()) return value; } } } if (xml.parent != nullptr) return getStyleAttribute (*xml.parent, attributeName, defaultValue); return defaultValue; } String getInheritedAttribute (const XmlPath& xml, StringRef attributeName) const { if (xml->hasAttribute (attributeName)) return xml->getStringAttribute (attributeName); if (xml.parent != nullptr) return getInheritedAttribute (*xml.parent, attributeName); return String(); } static int parsePlacementFlags (const String& align) noexcept { if (align.isEmpty()) return 0; if (align.containsIgnoreCase ("none")) return RectanglePlacement::stretchToFit; return (align.containsIgnoreCase ("slice") ? RectanglePlacement::fillDestination : 0) | (align.containsIgnoreCase ("xMin") ? RectanglePlacement::xLeft : (align.containsIgnoreCase ("xMax") ? RectanglePlacement::xRight : RectanglePlacement::xMid)) | (align.containsIgnoreCase ("yMin") ? RectanglePlacement::yTop : (align.containsIgnoreCase ("yMax") ? RectanglePlacement::yBottom : RectanglePlacement::yMid)); } //============================================================================== static bool isIdentifierChar (const juce_wchar c) { return CharacterFunctions::isLetter (c) || c == '-'; } static String getAttributeFromStyleList (const String& list, StringRef attributeName, const String& defaultValue) { int i = 0; for (;;) { i = list.indexOf (i, attributeName); if (i < 0) break; if ((i == 0 || (i > 0 && ! isIdentifierChar (list [i - 1]))) && ! isIdentifierChar (list [i + attributeName.length()])) { i = list.indexOfChar (i, ':'); if (i < 0) break; int end = list.indexOfChar (i, ';'); if (end < 0) end = 0x7ffff; return list.substring (i + 1, end).trim(); } ++i; } return defaultValue; } //============================================================================== static bool parseNextNumber (String::CharPointerType& text, String& value, const bool allowUnits) { String::CharPointerType s (text); while (s.isWhitespace() || *s == ',') ++s; String::CharPointerType start (s); if (s.isDigit() || *s == '.' || *s == '-') ++s; while (s.isDigit() || *s == '.') ++s; if ((*s == 'e' || *s == 'E') && ((s + 1).isDigit() || s[1] == '-' || s[1] == '+')) { s += 2; while (s.isDigit()) ++s; } if (allowUnits) while (s.isLetter()) ++s; if (s == start) { text = s; return false; } value = String (start, s); while (s.isWhitespace() || *s == ',') ++s; text = s; return true; } //============================================================================== static Colour parseColour (const String& s, int& index, const Colour defaultColour) { if (s [index] == '#') { uint32 hex[6] = { 0 }; int numChars = 0; for (int i = 6; --i >= 0;) { const int hexValue = CharacterFunctions::getHexDigitValue (s [++index]); if (hexValue >= 0) hex [numChars++] = (uint32) hexValue; else break; } if (numChars <= 3) return Colour ((uint8) (hex [0] * 0x11), (uint8) (hex [1] * 0x11), (uint8) (hex [2] * 0x11)); return Colour ((uint8) ((hex [0] << 4) + hex [1]), (uint8) ((hex [2] << 4) + hex [3]), (uint8) ((hex [4] << 4) + hex [5])); } if (s [index] == 'r' && s [index + 1] == 'g' && s [index + 2] == 'b') { const int openBracket = s.indexOfChar (index, '('); const int closeBracket = s.indexOfChar (openBracket, ')'); if (openBracket >= 3 && closeBracket > openBracket) { index = closeBracket; StringArray tokens; tokens.addTokens (s.substring (openBracket + 1, closeBracket), ",", ""); tokens.trim(); tokens.removeEmptyStrings(); if (tokens[0].containsChar ('%')) return Colour ((uint8) roundToInt (2.55 * tokens[0].getDoubleValue()), (uint8) roundToInt (2.55 * tokens[1].getDoubleValue()), (uint8) roundToInt (2.55 * tokens[2].getDoubleValue())); else return Colour ((uint8) tokens[0].getIntValue(), (uint8) tokens[1].getIntValue(), (uint8) tokens[2].getIntValue()); } } return Colours::findColourForName (s, defaultColour); } static AffineTransform parseTransform (String t) { AffineTransform result; while (t.isNotEmpty()) { StringArray tokens; tokens.addTokens (t.fromFirstOccurrenceOf ("(", false, false) .upToFirstOccurrenceOf (")", false, false), ", ", ""); tokens.removeEmptyStrings (true); float numbers[6]; for (int i = 0; i < numElementsInArray (numbers); ++i) numbers[i] = tokens[i].getFloatValue(); AffineTransform trans; if (t.startsWithIgnoreCase ("matrix")) { trans = AffineTransform (numbers[0], numbers[2], numbers[4], numbers[1], numbers[3], numbers[5]); } else if (t.startsWithIgnoreCase ("translate")) { trans = AffineTransform::translation (numbers[0], numbers[1]); } else if (t.startsWithIgnoreCase ("scale")) { trans = AffineTransform::scale (numbers[0], numbers[tokens.size() > 1 ? 1 : 0]); } else if (t.startsWithIgnoreCase ("rotate")) { trans = AffineTransform::rotation (degreesToRadians (numbers[0]), numbers[1], numbers[2]); } else if (t.startsWithIgnoreCase ("skewX")) { trans = AffineTransform::shear (std::tan (degreesToRadians (numbers[0])), 0.0f); } else if (t.startsWithIgnoreCase ("skewY")) { trans = AffineTransform::shear (0.0f, std::tan (degreesToRadians (numbers[0]))); } result = trans.followedBy (result); t = t.fromFirstOccurrenceOf (")", false, false).trimStart(); } return result; } static void endpointToCentreParameters (const double x1, const double y1, const double x2, const double y2, const double angle, const bool largeArc, const bool sweep, double& rx, double& ry, double& centreX, double& centreY, double& startAngle, double& deltaAngle) noexcept { const double midX = (x1 - x2) * 0.5; const double midY = (y1 - y2) * 0.5; const double cosAngle = std::cos (angle); const double sinAngle = std::sin (angle); const double xp = cosAngle * midX + sinAngle * midY; const double yp = cosAngle * midY - sinAngle * midX; const double xp2 = xp * xp; const double yp2 = yp * yp; double rx2 = rx * rx; double ry2 = ry * ry; const double s = (xp2 / rx2) + (yp2 / ry2); double c; if (s <= 1.0) { c = std::sqrt (jmax (0.0, ((rx2 * ry2) - (rx2 * yp2) - (ry2 * xp2)) / (( rx2 * yp2) + (ry2 * xp2)))); if (largeArc == sweep) c = -c; } else { const double s2 = std::sqrt (s); rx *= s2; ry *= s2; c = 0; } const double cpx = ((rx * yp) / ry) * c; const double cpy = ((-ry * xp) / rx) * c; centreX = ((x1 + x2) * 0.5) + (cosAngle * cpx) - (sinAngle * cpy); centreY = ((y1 + y2) * 0.5) + (sinAngle * cpx) + (cosAngle * cpy); const double ux = (xp - cpx) / rx; const double uy = (yp - cpy) / ry; const double vx = (-xp - cpx) / rx; const double vy = (-yp - cpy) / ry; const double length = juce_hypot (ux, uy); startAngle = acos (jlimit (-1.0, 1.0, ux / length)); if (uy < 0) startAngle = -startAngle; startAngle += double_Pi * 0.5; deltaAngle = acos (jlimit (-1.0, 1.0, ((ux * vx) + (uy * vy)) / (length * juce_hypot (vx, vy)))); if ((ux * vy) - (uy * vx) < 0) deltaAngle = -deltaAngle; if (sweep) { if (deltaAngle < 0) deltaAngle += double_Pi * 2.0; } else { if (deltaAngle > 0) deltaAngle -= double_Pi * 2.0; } deltaAngle = fmod (deltaAngle, double_Pi * 2.0); } template static bool findElementForId (const XmlPath& parent, const String& id, OperationType& op) { forEachXmlChildElement (*parent, e) { if (e->compareAttribute ("id", id)) { op (parent.getChild (e)); return true; } if (findElementForId (parent.getChild (e), id, op)) return true; } return false; } SVGState& operator= (const SVGState&) JUCE_DELETED_FUNCTION; }; //============================================================================== Drawable* Drawable::createFromSVG (const XmlElement& svgDocument) { SVGState state (&svgDocument); return state.parseSVGElement (SVGState::XmlPath (&svgDocument, nullptr)); } Path Drawable::parseSVGPath (const String& svgPath) { SVGState state (nullptr); Path p; state.parsePathString (p, svgPath); return p; } libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/filebrowser/000077500000000000000000000000001320201440200272455ustar00rootroot00000000000000juce_DirectoryContentsDisplayComponent.cpp000066400000000000000000000046021320201440200375750ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/filebrowser/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DirectoryContentsDisplayComponent::DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow) : fileList (listToShow) { } DirectoryContentsDisplayComponent::~DirectoryContentsDisplayComponent() { } //============================================================================== FileBrowserListener::~FileBrowserListener() { } void DirectoryContentsDisplayComponent::addListener (FileBrowserListener* const listener) { listeners.add (listener); } void DirectoryContentsDisplayComponent::removeListener (FileBrowserListener* const listener) { listeners.remove (listener); } void DirectoryContentsDisplayComponent::sendSelectionChangeMessage() { Component::BailOutChecker checker (dynamic_cast (this)); listeners.callChecked (checker, &FileBrowserListener::selectionChanged); } void DirectoryContentsDisplayComponent::sendMouseClickMessage (const File& file, const MouseEvent& e) { if (fileList.getDirectory().exists()) { Component::BailOutChecker checker (dynamic_cast (this)); listeners.callChecked (checker, &FileBrowserListener::fileClicked, file, e); } } void DirectoryContentsDisplayComponent::sendDoubleClickMessage (const File& file) { if (fileList.getDirectory().exists()) { Component::BailOutChecker checker (dynamic_cast (this)); listeners.callChecked (checker, &FileBrowserListener::fileDoubleClicked, file); } } juce_DirectoryContentsDisplayComponent.h000066400000000000000000000101761320201440200372450ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/filebrowser/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED #define JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED //============================================================================== /** A base class for components that display a list of the files in a directory. @see DirectoryContentsList */ class JUCE_API DirectoryContentsDisplayComponent { public: //============================================================================== /** Creates a DirectoryContentsDisplayComponent for a given list of files. */ DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow); /** Destructor. */ virtual ~DirectoryContentsDisplayComponent(); //============================================================================== /** Returns the number of files the user has got selected. @see getSelectedFile */ virtual int getNumSelectedFiles() const = 0; /** Returns one of the files that the user has currently selected. The index should be in the range 0 to (getNumSelectedFiles() - 1). @see getNumSelectedFiles */ virtual File getSelectedFile (int index) const = 0; /** Deselects any selected files. */ virtual void deselectAllFiles() = 0; /** Scrolls this view to the top. */ virtual void scrollToTop() = 0; /** If the specified file is in the list, it will become the only selected item (and if the file isn't in the list, all other items will be deselected). */ virtual void setSelectedFile (const File&) = 0; //============================================================================== /** Adds a listener to be told when files are selected or clicked. @see removeListener */ void addListener (FileBrowserListener* listener); /** Removes a listener. @see addListener */ void removeListener (FileBrowserListener* listener); //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the list. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds { highlightColourId = 0x1000540, /**< The colour to use to fill a highlighted row of the list. */ textColourId = 0x1000541, /**< The colour for the text. */ }; //============================================================================== /** @internal */ void sendSelectionChangeMessage(); /** @internal */ void sendDoubleClickMessage (const File& file); /** @internal */ void sendMouseClickMessage (const File& file, const MouseEvent& e); protected: //============================================================================== DirectoryContentsList& fileList; ListenerList listeners; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsDisplayComponent) }; #endif // JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED juce_DirectoryContentsList.cpp000066400000000000000000000162521320201440200352240ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/filebrowser/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ DirectoryContentsList::DirectoryContentsList (const FileFilter* f, TimeSliceThread& t) : fileFilter (f), thread (t), fileTypeFlags (File::ignoreHiddenFiles | File::findFiles), shouldStop (true) { } DirectoryContentsList::~DirectoryContentsList() { stopSearching(); } void DirectoryContentsList::setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles) { setTypeFlags (shouldIgnoreHiddenFiles ? (fileTypeFlags | File::ignoreHiddenFiles) : (fileTypeFlags & ~File::ignoreHiddenFiles)); } bool DirectoryContentsList::ignoresHiddenFiles() const { return (fileTypeFlags & File::ignoreHiddenFiles) != 0; } //============================================================================== void DirectoryContentsList::setDirectory (const File& directory, const bool includeDirectories, const bool includeFiles) { jassert (includeDirectories || includeFiles); // you have to speciify at least one of these! if (directory != root) { clear(); root = directory; changed(); // (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes) fileTypeFlags &= ~(File::findDirectories | File::findFiles); } int newFlags = fileTypeFlags; if (includeDirectories) newFlags |= File::findDirectories; else newFlags &= ~File::findDirectories; if (includeFiles) newFlags |= File::findFiles; else newFlags &= ~File::findFiles; setTypeFlags (newFlags); } void DirectoryContentsList::setTypeFlags (const int newFlags) { if (fileTypeFlags != newFlags) { fileTypeFlags = newFlags; refresh(); } } void DirectoryContentsList::stopSearching() { shouldStop = true; thread.removeTimeSliceClient (this); fileFindHandle = nullptr; } void DirectoryContentsList::clear() { stopSearching(); if (files.size() > 0) { files.clear(); changed(); } } void DirectoryContentsList::refresh() { clear(); if (root.isDirectory()) { fileFindHandle = new DirectoryIterator (root, false, "*", fileTypeFlags); shouldStop = false; thread.addTimeSliceClient (this); } } void DirectoryContentsList::setFileFilter (const FileFilter* newFileFilter) { const ScopedLock sl (fileListLock); fileFilter = newFileFilter; } //============================================================================== bool DirectoryContentsList::getFileInfo (const int index, FileInfo& result) const { const ScopedLock sl (fileListLock); if (const FileInfo* const info = files [index]) { result = *info; return true; } return false; } File DirectoryContentsList::getFile (const int index) const { const ScopedLock sl (fileListLock); if (const FileInfo* const info = files [index]) return root.getChildFile (info->filename); return File(); } bool DirectoryContentsList::contains (const File& targetFile) const { const ScopedLock sl (fileListLock); for (int i = files.size(); --i >= 0;) if (root.getChildFile (files.getUnchecked(i)->filename) == targetFile) return true; return false; } bool DirectoryContentsList::isStillLoading() const { return fileFindHandle != nullptr; } void DirectoryContentsList::changed() { sendChangeMessage(); } //============================================================================== int DirectoryContentsList::useTimeSlice() { const uint32 startTime = Time::getApproximateMillisecondCounter(); bool hasChanged = false; for (int i = 100; --i >= 0;) { if (! checkNextFile (hasChanged)) { if (hasChanged) changed(); return 500; } if (shouldStop || (Time::getApproximateMillisecondCounter() > startTime + 150)) break; } if (hasChanged) changed(); return 0; } bool DirectoryContentsList::checkNextFile (bool& hasChanged) { if (fileFindHandle != nullptr) { bool fileFoundIsDir, isHidden, isReadOnly; int64 fileSize; Time modTime, creationTime; if (fileFindHandle->next (&fileFoundIsDir, &isHidden, &fileSize, &modTime, &creationTime, &isReadOnly)) { if (addFile (fileFindHandle->getFile(), fileFoundIsDir, fileSize, modTime, creationTime, isReadOnly)) { hasChanged = true; } return true; } fileFindHandle = nullptr; } return false; } struct FileInfoComparator { static int compareElements (const DirectoryContentsList::FileInfo* const first, const DirectoryContentsList::FileInfo* const second) { #if JUCE_WINDOWS if (first->isDirectory != second->isDirectory) return first->isDirectory ? -1 : 1; #endif return first->filename.compareNatural (second->filename); } }; bool DirectoryContentsList::addFile (const File& file, const bool isDir, const int64 fileSize, Time modTime, Time creationTime, const bool isReadOnly) { const ScopedLock sl (fileListLock); if (fileFilter == nullptr || ((! isDir) && fileFilter->isFileSuitable (file)) || (isDir && fileFilter->isDirectorySuitable (file))) { ScopedPointer info (new FileInfo()); info->filename = file.getFileName(); info->fileSize = fileSize; info->modificationTime = modTime; info->creationTime = creationTime; info->isDirectory = isDir; info->isReadOnly = isReadOnly; for (int i = files.size(); --i >= 0;) if (files.getUnchecked(i)->filename == info->filename) return false; FileInfoComparator comp; files.addSorted (comp, info.release()); return true; } return false; } juce_DirectoryContentsList.h000066400000000000000000000204711320201440200346670ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/filebrowser/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED #define JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED //============================================================================== /** A class to asynchronously scan for details about the files in a directory. This keeps a list of files and some information about them, using a background thread to scan for more files. As files are found, it broadcasts change messages to tell any listeners. @see FileListComponent, FileBrowserComponent */ class JUCE_API DirectoryContentsList : public ChangeBroadcaster, private TimeSliceClient { public: //============================================================================== /** Creates a directory list. To set the directory it should point to, use setDirectory(), which will also start it scanning for files on the background thread. When the background thread finds and adds new files to this list, the ChangeBroadcaster class will send a change message, so you can register listeners and update them when the list changes. @param fileFilter an optional filter to select which files are included in the list. If this is nullptr, then all files and directories are included. Make sure that the filter doesn't get deleted during the lifetime of this object @param threadToUse a thread object that this list can use to scan for files as a background task. Make sure that the thread you give it has been started, or you won't get any files! */ DirectoryContentsList (const FileFilter* fileFilter, TimeSliceThread& threadToUse); /** Destructor. */ ~DirectoryContentsList(); //============================================================================== /** Returns the directory that's currently being used. */ const File& getDirectory() const noexcept { return root; } /** Sets the directory to look in for files. If the directory that's passed in is different to the current one, this will also start the background thread scanning it for files. */ void setDirectory (const File& directory, bool includeDirectories, bool includeFiles); /** Returns true if this list contains directories. @see setDirectory */ bool isFindingDirectories() const noexcept { return (fileTypeFlags & File::findDirectories) != 0; } /** Returns true if this list contains files. @see setDirectory */ bool isFindingFiles() const noexcept { return (fileTypeFlags & File::findFiles) != 0; } /** Clears the list, and stops the thread scanning for files. */ void clear(); /** Clears the list and restarts scanning the directory for files. */ void refresh(); /** True if the background thread hasn't yet finished scanning for files. */ bool isStillLoading() const; /** Tells the list whether or not to ignore hidden files. By default these are ignored. */ void setIgnoresHiddenFiles (bool shouldIgnoreHiddenFiles); /** Returns true if hidden files are ignored. @see setIgnoresHiddenFiles */ bool ignoresHiddenFiles() const; /** Replaces the current FileFilter. This can be nullptr to have no filter. The DirectoryContentList does not take ownership of this object - it just keeps a pointer to it, so you must manage its lifetime. Note that this only replaces the filter, it doesn't refresh the list - you'll probably want to call refresh() after calling this. */ void setFileFilter (const FileFilter* newFileFilter); //============================================================================== /** Contains cached information about one of the files in a DirectoryContentsList. */ struct FileInfo { //============================================================================== /** The filename. This isn't a full pathname, it's just the last part of the path, same as you'd get from File::getFileName(). To get the full pathname, use DirectoryContentsList::getDirectory().getChildFile (filename). */ String filename; /** File size in bytes. */ int64 fileSize; /** File modification time. As supplied by File::getLastModificationTime(). */ Time modificationTime; /** File creation time. As supplied by File::getCreationTime(). */ Time creationTime; /** True if the file is a directory. */ bool isDirectory; /** True if the file is read-only. */ bool isReadOnly; }; //============================================================================== /** Returns the number of files currently available in the list. The info about one of these files can be retrieved with getFileInfo() or getFile(). Obviously as the background thread runs and scans the directory for files, this number will change. @see getFileInfo, getFile */ int getNumFiles() const noexcept { return files.size(); } /** Returns the cached information about one of the files in the list. If the index is in-range, this will return true and will copy the file's details to the structure that is passed-in. If it returns false, then the index wasn't in range, and the structure won't be affected. @see getNumFiles, getFile */ bool getFileInfo (int index, FileInfo& resultInfo) const; /** Returns one of the files in the list. @param index should be less than getNumFiles(). If this is out-of-range, the return value will be File::nonexistent @see getNumFiles, getFileInfo */ File getFile (int index) const; /** Returns the file filter being used. The filter is specified in the constructor. */ const FileFilter* getFilter() const noexcept { return fileFilter; } /** Returns true if the list contains the specified file. */ bool contains (const File&) const; //============================================================================== /** @internal */ TimeSliceThread& getTimeSliceThread() const noexcept { return thread; } private: File root; const FileFilter* fileFilter; TimeSliceThread& thread; int fileTypeFlags; CriticalSection fileListLock; OwnedArray files; ScopedPointer fileFindHandle; bool volatile shouldStop; int useTimeSlice() override; void stopSearching(); void changed(); bool checkNextFile (bool& hasChanged); bool addFile (const File&, bool isDir, int64 fileSize, Time modTime, Time creationTime, bool isReadOnly); void setTypeFlags (int); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsList) }; #endif // JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED juce_FileBrowserComponent.cpp000066400000000000000000000423601320201440200350130ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/filebrowser/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ FileBrowserComponent::FileBrowserComponent (int flags_, const File& initialFileOrDirectory, const FileFilter* fileFilter_, FilePreviewComponent* previewComp_) : FileFilter (String::empty), fileFilter (fileFilter_), flags (flags_), previewComp (previewComp_), currentPathBox ("path"), fileLabel ("f", TRANS ("file:")), thread ("Juce FileBrowser") { // You need to specify one or other of the open/save flags.. jassert ((flags & (saveMode | openMode)) != 0); jassert ((flags & (saveMode | openMode)) != (saveMode | openMode)); // You need to specify at least one of these flags.. jassert ((flags & (canSelectFiles | canSelectDirectories)) != 0); String filename; if (initialFileOrDirectory == File::nonexistent) { currentRoot = File::getCurrentWorkingDirectory(); } else if (initialFileOrDirectory.isDirectory()) { currentRoot = initialFileOrDirectory; } else { chosenFiles.add (initialFileOrDirectory); currentRoot = initialFileOrDirectory.getParentDirectory(); filename = initialFileOrDirectory.getFileName(); } fileList = new DirectoryContentsList (this, thread); if ((flags & useTreeView) != 0) { FileTreeComponent* const tree = new FileTreeComponent (*fileList); fileListComponent = tree; if ((flags & canSelectMultipleItems) != 0) tree->setMultiSelectEnabled (true); addAndMakeVisible (tree); } else { FileListComponent* const list = new FileListComponent (*fileList); fileListComponent = list; list->setOutlineThickness (1); if ((flags & canSelectMultipleItems) != 0) list->setMultipleSelectionEnabled (true); addAndMakeVisible (list); } fileListComponent->addListener (this); addAndMakeVisible (currentPathBox); currentPathBox.setEditableText (true); resetRecentPaths(); currentPathBox.addListener (this); addAndMakeVisible (filenameBox); filenameBox.setMultiLine (false); filenameBox.setSelectAllWhenFocused (true); filenameBox.setText (filename, false); filenameBox.addListener (this); filenameBox.setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0); addAndMakeVisible (fileLabel); fileLabel.attachToComponent (&filenameBox, true); addAndMakeVisible (goUpButton = getLookAndFeel().createFileBrowserGoUpButton()); goUpButton->addListener (this); goUpButton->setTooltip (TRANS ("Go up to parent directory")); if (previewComp != nullptr) addAndMakeVisible (previewComp); setRoot (currentRoot); thread.startThread (4); } FileBrowserComponent::~FileBrowserComponent() { fileListComponent = nullptr; fileList = nullptr; thread.stopThread (10000); } //============================================================================== void FileBrowserComponent::addListener (FileBrowserListener* const newListener) { listeners.add (newListener); } void FileBrowserComponent::removeListener (FileBrowserListener* const listener) { listeners.remove (listener); } //============================================================================== bool FileBrowserComponent::isSaveMode() const noexcept { return (flags & saveMode) != 0; } int FileBrowserComponent::getNumSelectedFiles() const noexcept { if (chosenFiles.size() == 0 && currentFileIsValid()) return 1; return chosenFiles.size(); } File FileBrowserComponent::getSelectedFile (int index) const noexcept { if ((flags & canSelectDirectories) != 0 && filenameBox.getText().isEmpty()) return currentRoot; if (! filenameBox.isReadOnly()) return currentRoot.getChildFile (filenameBox.getText()); return chosenFiles[index]; } bool FileBrowserComponent::currentFileIsValid() const { const File f (getSelectedFile (0)); if (isSaveMode()) return (flags & canSelectDirectories) != 0 || ! f.isDirectory(); return f.exists(); } File FileBrowserComponent::getHighlightedFile() const noexcept { return fileListComponent->getSelectedFile (0); } void FileBrowserComponent::deselectAllFiles() { fileListComponent->deselectAllFiles(); } //============================================================================== bool FileBrowserComponent::isFileSuitable (const File& file) const { return (flags & canSelectFiles) != 0 && (fileFilter == nullptr || fileFilter->isFileSuitable (file)); } bool FileBrowserComponent::isDirectorySuitable (const File&) const { return true; } bool FileBrowserComponent::isFileOrDirSuitable (const File& f) const { if (f.isDirectory()) return (flags & canSelectDirectories) != 0 && (fileFilter == nullptr || fileFilter->isDirectorySuitable (f)); return (flags & canSelectFiles) != 0 && f.exists() && (fileFilter == nullptr || fileFilter->isFileSuitable (f)); } //============================================================================== const File& FileBrowserComponent::getRoot() const { return currentRoot; } void FileBrowserComponent::setRoot (const File& newRootDirectory) { bool callListeners = false; if (currentRoot != newRootDirectory) { callListeners = true; fileListComponent->scrollToTop(); String path (newRootDirectory.getFullPathName()); if (path.isEmpty()) path = File::separatorString; StringArray rootNames, rootPaths; getRoots (rootNames, rootPaths); if (! rootPaths.contains (path, true)) { bool alreadyListed = false; for (int i = currentPathBox.getNumItems(); --i >= 0;) { if (currentPathBox.getItemText (i).equalsIgnoreCase (path)) { alreadyListed = true; break; } } if (! alreadyListed) currentPathBox.addItem (path, currentPathBox.getNumItems() + 2); } } currentRoot = newRootDirectory; fileList->setDirectory (currentRoot, true, true); if (FileTreeComponent* tree = dynamic_cast (fileListComponent.get())) tree->refresh(); String currentRootName (currentRoot.getFullPathName()); if (currentRootName.isEmpty()) currentRootName = File::separatorString; currentPathBox.setText (currentRootName, dontSendNotification); goUpButton->setEnabled (currentRoot.getParentDirectory().isDirectory() && currentRoot.getParentDirectory() != currentRoot); if (callListeners) { Component::BailOutChecker checker (this); listeners.callChecked (checker, &FileBrowserListener::browserRootChanged, currentRoot); } } void FileBrowserComponent::setFileName (const String& newName) { filenameBox.setText (newName, true); fileListComponent->setSelectedFile (currentRoot.getChildFile (newName)); } void FileBrowserComponent::resetRecentPaths() { currentPathBox.clear(); StringArray rootNames, rootPaths; getRoots (rootNames, rootPaths); for (int i = 0; i < rootNames.size(); ++i) { if (rootNames[i].isEmpty()) currentPathBox.addSeparator(); else currentPathBox.addItem (rootNames[i], i + 1); } currentPathBox.addSeparator(); } void FileBrowserComponent::goUp() { setRoot (getRoot().getParentDirectory()); } void FileBrowserComponent::refresh() { fileList->refresh(); } void FileBrowserComponent::setFileFilter (const FileFilter* const newFileFilter) { if (fileFilter != newFileFilter) { fileFilter = newFileFilter; refresh(); } } String FileBrowserComponent::getActionVerb() const { return isSaveMode() ? ((flags & canSelectDirectories) != 0 ? TRANS("Choose") : TRANS("Save")) : TRANS("Open"); } void FileBrowserComponent::setFilenameBoxLabel (const String& name) { fileLabel.setText (name, dontSendNotification); } FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const noexcept { return previewComp; } DirectoryContentsDisplayComponent* FileBrowserComponent::getDisplayComponent() const noexcept { return fileListComponent; } //============================================================================== void FileBrowserComponent::resized() { getLookAndFeel() .layoutFileBrowserComponent (*this, fileListComponent, previewComp, ¤tPathBox, &filenameBox, goUpButton); } //============================================================================== void FileBrowserComponent::sendListenerChangeMessage() { Component::BailOutChecker checker (this); if (previewComp != nullptr) previewComp->selectedFileChanged (getSelectedFile (0)); // You shouldn't delete the browser when the file gets changed! jassert (! checker.shouldBailOut()); listeners.callChecked (checker, &FileBrowserListener::selectionChanged); } void FileBrowserComponent::selectionChanged() { StringArray newFilenames; bool resetChosenFiles = true; for (int i = 0; i < fileListComponent->getNumSelectedFiles(); ++i) { const File f (fileListComponent->getSelectedFile (i)); if (isFileOrDirSuitable (f)) { if (resetChosenFiles) { chosenFiles.clear(); resetChosenFiles = false; } chosenFiles.add (f); newFilenames.add (f.getRelativePathFrom (getRoot())); } } if (newFilenames.size() > 0) filenameBox.setText (newFilenames.joinIntoString (", "), false); sendListenerChangeMessage(); } void FileBrowserComponent::fileClicked (const File& f, const MouseEvent& e) { Component::BailOutChecker checker (this); listeners.callChecked (checker, &FileBrowserListener::fileClicked, f, e); } void FileBrowserComponent::fileDoubleClicked (const File& f) { if (f.isDirectory()) { setRoot (f); if ((flags & canSelectDirectories) != 0 && (flags & doNotClearFileNameOnRootChange) == 0) filenameBox.setText (String::empty); } else { Component::BailOutChecker checker (this); listeners.callChecked (checker, &FileBrowserListener::fileDoubleClicked, f); } } void FileBrowserComponent::browserRootChanged (const File&) {} bool FileBrowserComponent::keyPressed (const KeyPress& key) { (void) key; #if JUCE_LINUX || JUCE_WINDOWS if (key.getModifiers().isCommandDown() && (key.getKeyCode() == 'H' || key.getKeyCode() == 'h')) { fileList->setIgnoresHiddenFiles (! fileList->ignoresHiddenFiles()); fileList->refresh(); return true; } #endif return false; } //============================================================================== void FileBrowserComponent::textEditorTextChanged (TextEditor&) { sendListenerChangeMessage(); } void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor&) { if (filenameBox.getText().containsChar (File::separator)) { const File f (currentRoot.getChildFile (filenameBox.getText())); if (f.isDirectory()) { setRoot (f); chosenFiles.clear(); if ((flags & doNotClearFileNameOnRootChange) == 0) filenameBox.setText (String()); } else { setRoot (f.getParentDirectory()); chosenFiles.clear(); chosenFiles.add (f); filenameBox.setText (f.getFileName()); } } else { fileDoubleClicked (getSelectedFile (0)); } } void FileBrowserComponent::textEditorEscapeKeyPressed (TextEditor&) { } void FileBrowserComponent::textEditorFocusLost (TextEditor&) { if (! isSaveMode()) selectionChanged(); } //============================================================================== void FileBrowserComponent::buttonClicked (Button*) { goUp(); } void FileBrowserComponent::comboBoxChanged (ComboBox*) { const String newText (currentPathBox.getText().trim().unquoted()); if (newText.isNotEmpty()) { const int index = currentPathBox.getSelectedId() - 1; StringArray rootNames, rootPaths; getRoots (rootNames, rootPaths); if (rootPaths [index].isNotEmpty()) { setRoot (File (rootPaths [index])); } else { File f (newText); for (;;) { if (f.isDirectory()) { setRoot (f); break; } if (f.getParentDirectory() == f) break; f = f.getParentDirectory(); } } } } void FileBrowserComponent::getDefaultRoots (StringArray& rootNames, StringArray& rootPaths) { #if JUCE_WINDOWS Array roots; File::findFileSystemRoots (roots); rootPaths.clear(); for (int i = 0; i < roots.size(); ++i) { const File& drive = roots.getReference(i); String name (drive.getFullPathName()); rootPaths.add (name); if (drive.isOnHardDisk()) { String volume (drive.getVolumeLabel()); if (volume.isEmpty()) volume = TRANS("Hard Drive"); name << " [" << volume << ']'; } else if (drive.isOnCDRomDrive()) { name << " [" << TRANS("CD/DVD drive") << ']'; } rootNames.add (name); } rootPaths.add (String::empty); rootNames.add (String::empty); rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName()); rootNames.add (TRANS("Documents")); rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName()); rootNames.add (TRANS("Music")); rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName()); rootNames.add (TRANS("Pictures")); rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); rootNames.add (TRANS("Desktop")); #elif JUCE_MAC rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); rootNames.add (TRANS("Home folder")); rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName()); rootNames.add (TRANS("Documents")); rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName()); rootNames.add (TRANS("Music")); rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName()); rootNames.add (TRANS("Pictures")); rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); rootNames.add (TRANS("Desktop")); rootPaths.add (String::empty); rootNames.add (String::empty); Array volumes; File vol ("/Volumes"); vol.findChildFiles (volumes, File::findDirectories, false); for (int i = 0; i < volumes.size(); ++i) { const File& volume = volumes.getReference(i); if (volume.isDirectory() && ! volume.getFileName().startsWithChar ('.')) { rootPaths.add (volume.getFullPathName()); rootNames.add (volume.getFileName()); } } #else rootPaths.add ("/"); rootNames.add ("/"); rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); rootNames.add (TRANS("Home folder")); rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); rootNames.add (TRANS("Desktop")); #endif } void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPaths) { getDefaultRoots (rootNames, rootPaths); } juce_FileBrowserComponent.h000066400000000000000000000324731320201440200344640ustar00rootroot00000000000000libopenshot-audio-0.1.5/JuceLibraryCode/modules/juce_gui_basics/filebrowser/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ #ifndef JUCE_FILEBROWSERCOMPONENT_H_INCLUDED #define JUCE_FILEBROWSERCOMPONENT_H_INCLUDED //============================================================================== /** A component for browsing and selecting a file or directory to open or save. This contains a FileListComponent and adds various boxes and controls for navigating and selecting a file. It can work in different modes so that it can be used for loading or saving a file, or for choosing a directory. @see FileChooserDialogBox, FileChooser, FileListComponent */ class JUCE_API FileBrowserComponent : public Component, private FileBrowserListener, private TextEditorListener, private ButtonListener, private ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug) private FileFilter { public: //============================================================================== /** Various options for the browser. A combination of these is passed into the FileBrowserComponent constructor. */ enum FileChooserFlags { openMode = 1, /**< specifies that the component should allow the user to choose an existing file with the intention of opening it. */ saveMode = 2, /**< specifies that the component should allow the user to specify the name of a file that will be used to save something. */ canSelectFiles = 4, /**< specifies that the user can select files (can be used in conjunction with canSelectDirectories). */ canSelectDirectories = 8, /**< specifies that the user can select directories (can be used in conjuction with canSelectFiles). */ canSelectMultipleItems = 16, /**< specifies that the user can select multiple items. */ useTreeView = 32, /**< specifies that a tree-view should be shown instead of a file list. */ filenameBoxIsReadOnly = 64, /**< specifies that the user can't type directly into the filename box. */ warnAboutOverwriting = 128, /**< specifies that the dialog should warn about overwriting existing files (if possible). */ doNotClearFileNameOnRootChange = 256 /**< specifies that the file name should not be cleared upon root change. */ }; //============================================================================== /** Creates a FileBrowserComponent. @param flags A combination of flags from the FileChooserFlags enumeration, used to specify the component's behaviour. The flags must contain either openMode or saveMode, and canSelectFiles and/or canSelectDirectories. @param initialFileOrDirectory The file or directory that should be selected when the component begins. If this is File::nonexistent, a default directory will be chosen. @param fileFilter an optional filter to use to determine which files are shown. If this is nullptr then all files are displayed. Note that a pointer is kept internally to this object, so make sure that it is not deleted before the FileBrowserComponent object is deleted. @param previewComp an optional preview component that will be used to show previews of files that the user selects */ FileBrowserComponent (int flags, const File& initialFileOrDirectory, const FileFilter* fileFilter, FilePreviewComponent* previewComp); /** Destructor. */ ~FileBrowserComponent(); //============================================================================== /** Returns the number of files that the user has got selected. If multiple select isn't active, this will only be 0 or 1. To get the complete list of files they've chosen, pass an index to getCurrentFile(). */ int getNumSelectedFiles() const noexcept; /** Returns one of the files that the user has chosen. If the box has multi-select enabled, the index lets you specify which of the files to get - see getNumSelectedFiles() to find out how many files were chosen. @see getHighlightedFile */ File getSelectedFile (int index) const noexcept; /** Deselects any files that are currently selected. */ void deselectAllFiles(); /** Returns true if the currently selected file(s) are usable. This can be used to decide whether the user can press "ok" for the current file. What it does depends on the mode, so for example in an "open" mode, this only returns true if a file has been selected and if it exists. In a "save" mode, a non-existent file would also be valid. */ bool currentFileIsValid() const; /** This returns the last item in the view that the user has highlighted. This may be different from getCurrentFile(), which returns the value that is shown in the filename box, and if there are multiple selections, this will only return one of them. @see getSelectedFile */ File getHighlightedFile() const noexcept; //============================================================================== /** Returns the directory whose contents are currently being shown in the listbox. */ const File& getRoot() const; /** Changes the directory that's being shown in the listbox. */ void setRoot (const File& newRootDirectory); /** Changes the name that is currently shown in the filename box. */ void setFileName (const String& newName); /** Equivalent to pressing the "up" button to browse the parent directory. */ void goUp(); /** Refreshes the directory that's currently being listed. */ void refresh(); /** Changes the filter that's being used to sift the files. */ void setFileFilter (const FileFilter* newFileFilter); /** Returns a verb to describe what should happen when the file is accepted. E.g. if browsing in "load file" mode, this will be "Open", if in "save file" mode, it'll be "Save", etc. */ virtual String getActionVerb() const; /** Returns true if the saveMode flag was set when this component was created. */ bool isSaveMode() const noexcept; /** Sets the label that will be displayed next to the filename entry box. By default this is just "file", but you might want to change it to something more appropriate for your app. */ void setFilenameBoxLabel (const String& name); //============================================================================== /** Adds a listener to be told when the user selects and clicks on files. @see removeListener */ void addListener (FileBrowserListener* listener); /** Removes a listener. @see addListener */ void removeListener (FileBrowserListener* listener); /** Returns a platform-specific list of names and paths for some suggested places the user might want to use as root folders. The list returned contains empty strings to indicate section breaks. @see getRoots() */ static void getDefaultRoots (StringArray& rootNames, StringArray& rootPaths); //============================================================================== /** This abstract base class is implemented by LookAndFeel classes to provide various file-browser layout and drawing methods. */ struct JUCE_API LookAndFeelMethods { virtual ~LookAndFeelMethods() {} // These return a pointer to an internally cached drawable - make sure you don't keep // a copy of this pointer anywhere, as it may become invalid in the future. virtual const Drawable* getDefaultFolderImage() = 0; virtual const Drawable* getDefaultDocumentFileImage() = 0; virtual AttributedString createFileChooserHeaderText (const String& title, const String& instructions) = 0; virtual void drawFileBrowserRow (Graphics&, int width, int height, const String& filename, Image* optionalIcon, const String& fileSizeDescription, const String& fileTimeDescription, bool isDirectory, bool isItemSelected, int itemIndex, DirectoryContentsDisplayComponent&) = 0; virtual Button* createFileBrowserGoUpButton() = 0; virtual void layoutFileBrowserComponent (FileBrowserComponent& browserComp, DirectoryContentsDisplayComponent* fileListComponent, FilePreviewComponent* previewComp, ComboBox* currentPathBox, TextEditor* filenameBox, Button* goUpButton) = 0; }; //============================================================================== /** @internal */ void resized() override; /** @internal */ void buttonClicked (Button*) override; /** @internal */ void comboBoxChanged (ComboBox*) override; /** @internal */ void textEditorTextChanged (TextEditor&) override; /** @internal */ void textEditorReturnKeyPressed (TextEditor&) override; /** @internal */ void textEditorEscapeKeyPressed (TextEditor&) override; /** @internal */ void textEditorFocusLost (TextEditor&) override; /** @internal */ bool keyPressed (const KeyPress&) override; /** @internal */ void selectionChanged() override; /** @internal */ void fileClicked (const File&, const MouseEvent&) override; /** @internal */ void fileDoubleClicked (const File&) override; /** @internal */ void browserRootChanged (const File&) override; /** @internal */ bool isFileSuitable (const File&) const override; /** @internal */ bool isDirectorySuitable (const File&) const override; /** @internal */ FilePreviewComponent* getPreviewComponent() const noexcept; /** @internal */ DirectoryContentsDisplayComponent* getDisplayComponent() const noexcept; protected: /** Returns a list of names and paths for the default places the user might want to look. By default this just calls getDefaultRoots(), but you may want to override it to return a custom list. */ virtual void getRoots (StringArray& rootNames, StringArray& rootPaths); /** Updates the items in the dropdown list of recent paths with the values from getRoots(). */ void resetRecentPaths(); private: //============================================================================== ScopedPointer fileList; const FileFilter* fileFilter; int flags; File currentRoot; Array chosenFiles; ListenerList listeners; ScopedPointer fileListComponent; FilePreviewComponent* previewComp; ComboBox currentPathBox; TextEditor filenameBox; Label fileLabel; ScopedPointer