├── icon.png ├── .gitignore ├── .gitattributes ├── LICENSE.md ├── README.md ├── QuickStart.md ├── Fluid_Decomposition_Overview.md ├── scripts ├── target_post.cmake └── MakeCLIStub.cmake ├── .github └── workflows │ ├── nightly.yaml │ └── release.yml ├── .clang-format ├── CMakeLists.txt ├── include └── FluidCLIWrapper.hpp └── testing-code └── slice-reader.maxpat /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucoma/flucoma-cli/HEAD/icon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | build* 3 | winbuild 4 | **/build/* 5 | **/.DS_Store 6 | release-packaging/ 7 | docs 8 | .vscode 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.yml text 4 | *.gitignore text 5 | *.maxpat text 6 | *.hpp text 7 | *.cmake text 8 | *.md text 9 | *.txt text 10 | *.yaml text 11 | 12 | *.png binary 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright University of Huddersfield 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fluid Corpus Manipulation: Command line interface 2 | 3 | This repository hosts code for generating the command line executables and documentation resources for the Fluid Corpus Manipulation Project. Much of the actual code that does the exciting stuff lives in this repository's principal dependency, the [Fluid Corpus Manipulation Library](https://github.com/flucoma/flucoma-core). 4 | 5 | * A wrapper from our code that allows us to generate command-line executables from a generic class. 6 | * Stubs for producing an executable for each 'client' in the Fluid Corpus Manipulation Library. 7 | * CMake code for managing dependencies, building and packaging. 8 | 9 | # Minimal Quick Build 10 | 11 | Minimal build steps below. For detailed guidance see https://github.com/flucoma/flucoma-cli/wiki/Compiling 12 | 13 | ## Prerequisites 14 | 15 | * C++14 compliant compiler (clang, GCC or MSVC) 16 | * cmake 17 | * make (or Ninja or XCode or VisualStudio) 18 | * git 19 | * an internet connection 20 | 21 | CMake will automatically download the dependencies needed 22 | 23 | ```bash 24 | mkdir -p build && cd build 25 | cmake .. 26 | make install 27 | ``` 28 | 29 | This will assemble a package in `release-packaging`. 30 | 31 | Alternatively, flucoma-cli is now on the [AUR / Arch User Repository](https://aur.archlinux.org/packages/flucoma-cli-git/) and can now be compiled and downloaded by executing: 32 | 33 | ```bash 34 | yay -S flucoma-cli-git 35 | ``` 36 | 37 | on Arch Linux and Manjaro (with thanks to @madskjeldgaard) 38 | 39 | ## Credits 40 | #### FluCoMa core development team (in alphabetical order) 41 | Owen Green, Gerard Roma, Pierre Alexandre Tremblay 42 | 43 | #### Other contributors: 44 | Alex Harker, Francesco Cameli 45 | 46 | > This project has received funding from the European Research Council (ERC) under the European Union's Horizon 2020 research and innovation programme (grant agreement No 725899). 47 | -------------------------------------------------------------------------------- /QuickStart.md: -------------------------------------------------------------------------------- 1 | # Instructions for the Command-Line Interface version of the Fluid.buf* algorithms 2 | 3 | ## Note: if you never used the Terminal, this is not for you yet, as these instructions are very terse. 4 | 5 | ## How to start: 6 | 7 | 1) copy the content of /bin wherever you fancy 8 | 9 | 2) if you have not copied it in one of your /bin folders, add the path of where it is to your shell $PATH by typing: 10 | ``` 11 | export PATH="XXX:$PATH" 12 | ``` 13 | where XXX is the complete path to the folder that contains your FluCoMa-CLI apps. 14 | 15 | 3) Use the CLI by entering the function and a full list of arguments, preceded with the hyphen (-). Default values are in place, but you need at least a source and destination paths. These need to be full paths. 16 | 17 | For instance: 18 | ``` 19 | fluid-hpss -source FULLPATHTOASOUND -harmonic ~/Desktop/harm.wav -percussive ~/Desktop/perc.wav 20 | ``` 21 | 22 | will put on the Desktop 2 files from the default HPSS settings, and: 23 | 24 | ``` 25 | fluid-nmf -source FULLPATHTOASOUND -components 5 -resynth ~/Desktop/components.wav 26 | ``` 27 | 28 | will put on the desktop a single 5-channel audio file with the resynthesised 5 components requested. 29 | 30 | 4) an overview of the provided functions is available in the Fluid_Decomposition_Overview.md file, and an extensive documentation is available as a webpage in the /docs folder 31 | 32 | #### Enjoy! 33 | 34 | 35 | ## Known limitations: 36 | - only outputs WAV 32bit float files for now. 37 | - can't currently read some .wav files (with a WAVE EXTENSIBLE header) 38 | - does not support relative paths. 39 | - no man page yet: references needs to be taken from the provided documentation in /docs and only a one-liner description of parameters is provided with -h 40 | 41 | > This project has received funding from the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899). 42 | -------------------------------------------------------------------------------- /Fluid_Decomposition_Overview.md: -------------------------------------------------------------------------------- 1 | # Fluid Decomposition: play with sonic slices, layers and objects 2 | 3 | The Fluid Decomposition toolbox provides an open-ended, loosely coupled set of objects to break up and analyse sound in terms of slices (segments in time), layers (superpositions in time and frequency) and objects (configurable or discoverable patterns in sound). 4 | 5 | ## File Formats 6 | The programs can read audio in Wav or AIFF format, and write either audio or CSV files (determined by the extension given to the relevant argument). 7 | 8 | ## Slices 9 | #### `fluid-ampslice` and `fluid-ampgate` 10 | Slice by amplitude envelope 11 | 12 | #### `fluid-onsetslice` 13 | Slice by onsets in the spectral domain 14 | 15 | #### `fluid-noveltyslice` 16 | Slice by estimated novelty on a choice of features 17 | 18 | #### `fluid-transientslice` 19 | Slice by transient modelling 20 | 21 | ## Layers 22 | #### `fluid-sines` 23 | Decompose into sines + residual 24 | 25 | #### `fluid-transients` 26 | Decompose into transients + residual 27 | 28 | #### `fluid-hpss` 29 | Decompose into 'harmonic' and 'percussive' layers 30 | 31 | ## Objects 32 | #### `fluid-nmf` 33 | Use Nonnegative Matrix Factorisation to explore and decompose sounds 34 | 35 | ## Descriptors 36 | #### `fluid-loudness` 37 | Report amplitude and peak, or r128 loudness and true peak 38 | 39 | #### `fluid-pitch` 40 | A suite of pitch estimators 41 | 42 | #### `fluid-melbands` 43 | Energy across Mel bands 44 | 45 | #### `fluid-mfcc` 46 | Mel Frequency Ceptstral Coefficients 47 | 48 | #### `fluid-spectralshape` 49 | Summary measures of spectral shape 50 | 51 | #### `fluid-stats` 52 | Statistics of files 53 | 54 | 55 | This toolbox was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899). 56 | -------------------------------------------------------------------------------- /scripts/target_post.cmake: -------------------------------------------------------------------------------- 1 | # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) 2 | # Copyright University of Huddersfield. 3 | # Licensed under the BSD-3 License. 4 | # See license.md file in the project root for full license information. 5 | # This project has received funding from the European Research Council (ERC) 6 | # under the European Union’s Horizon 2020 research and innovation programme 7 | # (grant agreement No 725899). 8 | 9 | 10 | if(MSVC) 11 | foreach(flag_var 12 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 13 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 14 | if(${flag_var} MATCHES "/MD") 15 | string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") 16 | endif() 17 | endforeach() 18 | endif() 19 | 20 | target_link_libraries(${PROG} 21 | PRIVATE 22 | FLUID_DECOMPOSITION 23 | FLUID_CLI_WRAPPER 24 | ) 25 | 26 | target_include_directories( 27 | ${PROG} 28 | PRIVATE 29 | "${FLUID_VERSION_PATH}" 30 | ) 31 | 32 | if (APPLE) 33 | #targeting <= 10.9, need to explicitly set libc++ 34 | target_compile_options(${PROG} PRIVATE -stdlib=libc++) 35 | target_link_libraries(${PROG} PRIVATE -stdlib=libc++) 36 | endif() 37 | 38 | #set AVX, or whatever 39 | if(DEFINED FLUID_ARCH) 40 | target_compile_options(${PROG} PRIVATE ${FLUID_ARCH}) 41 | endif() 42 | 43 | if(MSVC) 44 | target_compile_options(${PROG} PRIVATE -D_USE_MATH_DEFINES /W3) 45 | else() 46 | target_compile_options(${PROG} PRIVATE -Wall -Wextra -Wpedantic -Wreturn-type -Wno-conversion) 47 | endif(MSVC) 48 | 49 | set_target_properties( 50 | ${PROG} 51 | PROPERTIES 52 | RUNTIME_OUTPUT_DIRECTORY ${FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY} 53 | RUNTIME_OUTPUT_DIRECTORY_RELEASE ${FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY} 54 | RUNTIME_OUTPUT_DIRECTORY_DEBUG ${FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY} 55 | ) 56 | 57 | get_property(HEADERS TARGET FLUID_DECOMPOSITION PROPERTY INTERFACE_SOURCES) 58 | source_group(TREE "${flucoma-core_SOURCE_DIR}/include" FILES ${HEADERS}) 59 | -------------------------------------------------------------------------------- /scripts/MakeCLIStub.cmake: -------------------------------------------------------------------------------- 1 | # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) 2 | # Copyright University of Huddersfield. 3 | # Licensed under the BSD-3 License. 4 | # See license.md file in the project root for full license information. 5 | # This project has received funding from the European Research Council (ERC) 6 | # under the European Union’s Horizon 2020 research and innovation programme 7 | # (grant agreement No 725899). 8 | 9 | cmake_minimum_required(VERSION 3.18) 10 | 11 | include(FluidClientStub) 12 | 13 | function(make_external_name client header output-var) 14 | string(REPLACE "Buf" "fluid-" client ${client}) 15 | string(TOLOWER ${client} client) 16 | set(${output-var} ${client} PARENT_SCOPE) 17 | endfunction() 18 | 19 | function (add_cli_binary name source) 20 | 21 | add_executable(${name} ${source}) 22 | 23 | # if(MSVC) 24 | # foreach(flag_var 25 | # CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 26 | # CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 27 | # if(${flag_var} MATCHES "/MD") 28 | # string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") 29 | # endif() 30 | # endforeach() 31 | # endif() 32 | 33 | target_link_libraries(${name} 34 | PRIVATE 35 | FLUID_DECOMPOSITION 36 | FLUID_CLI_WRAPPER 37 | ) 38 | 39 | target_include_directories( 40 | ${name} 41 | PRIVATE 42 | "${FLUID_VERSION_PATH}" 43 | ) 44 | 45 | if (APPLE) 46 | #targeting <= 10.9, need to explicitly set libc++ 47 | target_compile_options(${name} PRIVATE -stdlib=libc++) 48 | target_link_libraries(${name} PRIVATE -stdlib=libc++) 49 | endif() 50 | 51 | if(MSVC) 52 | target_compile_options(${name} PRIVATE -D_USE_MATH_DEFINES /external:W0 /W3 /bigobj) 53 | else() 54 | target_compile_options(${name} PRIVATE -Wall -Wextra -Wpedantic -Wreturn-type -Wno-conversion) 55 | endif(MSVC) 56 | 57 | set_target_properties( 58 | ${name} 59 | PROPERTIES 60 | RUNTIME_OUTPUT_DIRECTORY ${FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY} 61 | RUNTIME_OUTPUT_DIRECTORY_RELEASE ${FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY} 62 | RUNTIME_OUTPUT_DIRECTORY_DEBUG ${FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY} 63 | MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" 64 | ) 65 | 66 | get_property(HEADERS TARGET FLUID_DECOMPOSITION PROPERTY INTERFACE_SOURCES) 67 | source_group(TREE "${flucoma-core_SOURCE_DIR}/include" FILES ${HEADERS}) 68 | 69 | endfunction() 70 | 71 | function(generate_cli_source) 72 | # # Define the supported set of keywords 73 | set(noValues "") 74 | set(singleValues FILENAME EXTERNALS_OUT FILE_OUT) 75 | set(multiValues CLIENTS HEADERS CLASSES) 76 | # # Process the arguments passed in 77 | include(CMakeParseArguments) 78 | cmake_parse_arguments(ARG 79 | "${noValues}" 80 | "${singleValues}" 81 | "${multiValues}" 82 | ${ARGN}) 83 | 84 | if(ARG_FILENAME) 85 | set(external_name ${ARG_FILENAME}) 86 | else() 87 | list(GET ARG_CLIENTS 0 client_name) 88 | list(GET ARG_HEADERS 0 header) 89 | make_external_name(${client_name} ${header} external_name) 90 | endif() 91 | 92 | set(ENTRY_POINT "int main(int argc, const char* argv[])") 93 | set(WRAPPER_TEMPLATE [=[CLIWrapper<${class}>::run(argc, argv);]=]) 94 | set(CCE_WRAPPER "#include \"FluidCLIWrapper.hpp\"") 95 | 96 | generate_source(${ARGN} EXTERNALS_OUT external FILE_OUT outfile) 97 | 98 | message(STATUS "Generating: ${external_name}") 99 | add_cli_binary(${external_name} ${outfile}) 100 | endfunction() 101 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yaml: -------------------------------------------------------------------------------- 1 | name: nightly 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ main ] 7 | 8 | concurrency: 9 | group: environment-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | windows: 14 | runs-on: windows-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: flucoma/actions/env@main 18 | - uses: flucoma/actions/cli@main 19 | with: 20 | branch: origin/main 21 | 22 | - name: compress archive 23 | run: 7z a FluCoMa-CLI-Windows-x64-nightly.zip FluidCorpusManipulation 24 | working-directory: release-packaging 25 | 26 | - uses: actions/upload-artifact@v4.3.6 27 | with: 28 | name: winbuild 29 | path: release-packaging/FluCoMa-CLI-Windows-x64-nightly.zip 30 | 31 | macos: 32 | runs-on: macos-latest 33 | steps: 34 | - uses: actions/checkout@v3 35 | - uses: flucoma/actions/env@main 36 | - uses: flucoma/actions/cli@main 37 | with: 38 | branch: origin/main 39 | 40 | - name: sign binaries 41 | uses: flucoma/actions/distribution@main 42 | with: 43 | glob: 'fluid -t x' 44 | package: 'release-packaging' 45 | codesign_options: 'runtime' 46 | output_type: 'dmg' 47 | output: FluCoMa-CLI-Mac-nightly 48 | cert: ${{ secrets.CERT }} 49 | certpwd: ${{ secrets.CERTPWD }} 50 | teamid: ${{ secrets.WWDRTEAMID }} 51 | apppwd: ${{ secrets.APPSTORECONNECTPWD }} 52 | appusr: ${{ secrets.APPSTORECONNECTUSERNAME }} 53 | 54 | - uses: actions/upload-artifact@v4.3.6 55 | with: 56 | name: macbuild 57 | path: release-packaging/FluCoMa-CLI-Mac-nightly.dmg 58 | 59 | linux: 60 | runs-on: ubuntu-22.04 61 | steps: 62 | - uses: actions/checkout@v3 63 | - uses: flucoma/actions/env@main 64 | - uses: flucoma/actions/cli@main 65 | with: 66 | branch: origin/main 67 | 68 | - name: compress archive 69 | run: tar -zcvf FluCoMa-CLI-Linux-x64-nightly.tar.gz FluidCorpusManipulation 70 | working-directory: release-packaging 71 | 72 | - uses: actions/upload-artifact@v4.3.6 73 | with: 74 | name: linuxbuild 75 | path: release-packaging/FluCoMa-CLI-Linux-x64-nightly.tar.gz 76 | 77 | release: 78 | runs-on: ubuntu-22.04 79 | needs: [windows, linux, macos] 80 | steps: 81 | - uses: actions/download-artifact@v4.1.7 82 | with: 83 | name: linuxbuild 84 | 85 | - uses: actions/download-artifact@v4.1.7 86 | with: 87 | name: macbuild 88 | 89 | - uses: actions/download-artifact@v4.1.7 90 | with: 91 | name: winbuild 92 | 93 | - uses: dev-drprasad/delete-tag-and-release@v0.2.1 94 | with: 95 | delete_release: true # default: false 96 | tag_name: nightly # tag name to delete 97 | env: 98 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 99 | 100 | - name: create release 101 | uses: ncipollo/release-action@v1 102 | with: 103 | name: FluCoMa CLI Nightly Release 104 | artifacts: "FluCoMa*" 105 | body: "This is a nightly build of the FluCoMa CLI tools. As such, be warned there may be bugs or other unexpected behaviour. The build hash is ${{ github.sha }}" 106 | tag: nightly 107 | prerelease: true 108 | draft: false 109 | allowUpdates: true 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | windows: 7 | runs-on: windows-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: flucoma/actions/env@main 11 | - uses: flucoma/actions/cli@main 12 | with: 13 | branch: origin/${{ github.ref_name }} 14 | 15 | - name: compress archive 16 | run: 7z a FluCoMa-CLI-Windows-x64.zip FluidCorpusManipulation 17 | working-directory: release-packaging 18 | 19 | - uses: actions/upload-artifact@v4.3.6 20 | with: 21 | name: winbuild 22 | path: release-packaging/FluCoMa-CLI-Windows-x64.zip 23 | 24 | mac: 25 | runs-on: macos-latest 26 | steps: 27 | - uses: actions/checkout@v3 28 | - uses: flucoma/actions/env@main 29 | - uses: flucoma/actions/cli@main 30 | with: 31 | branch: origin/${{ github.ref_name }} 32 | 33 | - name: sign binaries 34 | uses: flucoma/actions/distribution@main 35 | with: 36 | glob: 'fluid -t x' 37 | package: 'release-packaging' 38 | codesign_options: 'runtime' 39 | output_type: 'dmg' 40 | output: FluCoMa-CLI-Mac 41 | cert: ${{ secrets.CERT }} 42 | certpwd: ${{ secrets.CERTPWD }} 43 | teamid: ${{ secrets.WWDRTEAMID }} 44 | apppwd: ${{ secrets.APPSTORECONNECTPWD }} 45 | appusr: ${{ secrets.APPSTORECONNECTUSERNAME }} 46 | 47 | - uses: actions/upload-artifact@v4.3.6 48 | with: 49 | name: macbuild 50 | path: release-packaging/FluCoMa-CLI-Mac.dmg 51 | 52 | linux: 53 | runs-on: ubuntu-22.04 54 | outputs: 55 | version: ${{ steps.get-version.outputs.version }} 56 | steps: 57 | - uses: actions/checkout@v3 58 | - uses: flucoma/actions/env@main 59 | - uses: flucoma/actions/cli@main 60 | with: 61 | branch: origin/${{ github.ref_name }} 62 | 63 | - name: compress archive 64 | run: tar -zcvf FluCoMa-CLI-Linux-x64.tar.gz FluidCorpusManipulation 65 | working-directory: release-packaging 66 | 67 | - uses: actions/upload-artifact@v4.3.6 68 | with: 69 | name: linuxbuild 70 | path: release-packaging/FluCoMa-CLI-Linux-x64.tar.gz 71 | 72 | - run: ls -r 73 | 74 | - id: get-version 75 | run: echo "version=$(cat flucoma.version.rc)" >> $GITHUB_OUTPUT 76 | working-directory: build/_deps/flucoma-core-src 77 | 78 | release: 79 | runs-on: ubuntu-22.04 80 | needs: [mac, windows, linux] 81 | 82 | steps: 83 | - uses: actions/download-artifact@v4.1.7 84 | with: 85 | name: linuxbuild 86 | 87 | - uses: actions/download-artifact@v4.1.7 88 | with: 89 | name: macbuild 90 | 91 | - uses: actions/download-artifact@v4.1.7 92 | with: 93 | name: winbuild 94 | 95 | #### UPLOAD RELEASE #### 96 | 97 | - name: delete pre-existing release 98 | uses: dev-drprasad/delete-tag-and-release@v0.2.1 99 | with: 100 | delete_release: true # default: false 101 | tag_name: ${{ needs.linux.outputs.version }} # tag name to delete 102 | env: 103 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 104 | 105 | - name: package and upload 106 | uses: softprops/action-gh-release@v1 107 | with: 108 | name: ${{ needs.linux.outputs.version }} 109 | body: "This is a release build of the FluCoMa CLI tools. The build hash is ${{ github.sha }}" 110 | files: FluCoMa* 111 | prerelease: true 112 | tag_name: ${{ needs.linux.outputs.version }} 113 | target_commitish: ${{ github.sha }} 114 | draft: false 115 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: true 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: true 13 | AllowShortCaseLabelsOnASingleLine: true 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: true 25 | AfterControlStatement: true 26 | AfterEnum: false 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: true 31 | AfterUnion: true 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: true 35 | IndentBraces: false 36 | SplitEmptyFunction: false 37 | SplitEmptyRecord: false 38 | SplitEmptyNamespace: false 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Custom 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 80 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Merge 63 | # Try and group includes by 'locality' 64 | # We use "" with rel paths for headers in same repo 65 | # Headers in quotes wiht no relative path at top 66 | # Group ../ and ../../ with different priorities, so they get sorted separately 67 | # <> headers *with a folder break* likely not to be STL -> STL / systems headers last 68 | IncludeCategories: 69 | - Regex: '<.+/' 70 | Priority: 4 71 | - Regex: '"../../' 72 | Priority: 3 73 | - Regex: '../' # 74 | Priority: 2 75 | - Regex: '".+hpp"' 76 | Priority: 1 77 | IncludeIsMainRegex: '(Test)?$' 78 | IndentCaseLabels: false 79 | IndentPPDirectives: None 80 | IndentWidth: 2 81 | IndentWrappedFunctionNames: false 82 | JavaScriptQuotes: Leave 83 | JavaScriptWrapImports: true 84 | KeepEmptyLinesAtTheStartOfBlocks: true 85 | MacroBlockBegin: '' 86 | MacroBlockEnd: '' 87 | MaxEmptyLinesToKeep: 2 88 | NamespaceIndentation: None 89 | ObjCBinPackProtocolList: Auto 90 | ObjCBlockIndentWidth: 2 91 | ObjCSpaceAfterProperty: false 92 | ObjCSpaceBeforeProtocolList: true 93 | PenaltyBreakAssignment: 2 94 | PenaltyBreakBeforeFirstCallParameter: 19 95 | PenaltyBreakComment: 300 96 | PenaltyBreakFirstLessLess: 120 97 | PenaltyBreakString: 1000 98 | PenaltyExcessCharacter: 1000000 99 | PenaltyReturnTypeOnItsOwnLine: 60 100 | PointerAlignment: Left 101 | ReflowComments: true 102 | SortIncludes: true 103 | SortUsingDeclarations: true 104 | SpaceAfterCStyleCast: true 105 | SpaceAfterTemplateKeyword: true 106 | SpaceBeforeAssignmentOperators: true 107 | SpaceBeforeCtorInitializerColon: true 108 | SpaceBeforeInheritanceColon: true 109 | SpaceBeforeParens: ControlStatements 110 | SpaceBeforeRangeBasedForLoopColon: true 111 | SpaceInEmptyParentheses: false 112 | SpacesBeforeTrailingComments: 1 113 | SpacesInAngles: false 114 | SpacesInContainerLiterals: true 115 | SpacesInCStyleCastParentheses: false 116 | SpacesInParentheses: false 117 | SpacesInSquareBrackets: false 118 | Standard: Cpp11 119 | TabWidth: 8 120 | UseTab: Never 121 | ... 122 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) 2 | # Copyright University of Huddersfield. 3 | # Licensed under the BSD-3 License. 4 | # See license.md file in the project root for full license information. 5 | # This project has received funding from the European Research Council (ERC) 6 | # under the European Union’s Horizon 2020 research and innovation programme 7 | # (grant agreement No 725899). 8 | 9 | cmake_minimum_required(VERSION 3.18) 10 | 11 | set(CMAKE_CXX_STANDARD 17) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | set(CMAKE_CXX_EXTENSIONS OFF) 14 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 15 | 16 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") 17 | 18 | set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/release-packaging" CACHE PATH "") 19 | 20 | set(FLUID_BRANCH "origin/main" CACHE STRING "Branch to pull flucoma dependencies from") 21 | set(FLUID_PATH "" CACHE PATH "Optional path to the flucoma-core repo; will be downloaded if absent") 22 | option(DOCS "Generate HTML documentaiton" OFF) 23 | set(FLUID_DOCS_PATH "" CACHE PATH "Optional path to flucoma-docs; will be downloaded if absent") 24 | 25 | if (APPLE) 26 | set(CMAKE_XCODE_GENERATE_SCHEME ON) 27 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "") 28 | #A consequence of targetting 10.8. Needs to be set globally from 10.15 onwards in order for the test program to compile successfully during configure 29 | string(APPEND CMAKE_CXX_FLAGS " -stdlib=libc++") 30 | endif() 31 | 32 | project (flucoma-cli LANGUAGES CXX) 33 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 34 | set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY ON) 35 | 36 | include(FetchContent) 37 | 38 | FetchContent_Declare( 39 | flucoma-core 40 | GIT_REPOSITORY https://github.com/flucoma/flucoma-core.git 41 | GIT_PROGRESS TRUE 42 | GIT_TAG ${FLUID_BRANCH} 43 | ) 44 | 45 | FetchContent_Declare( 46 | flucoma-docs 47 | GIT_REPOSITORY https://github.com/flucoma/flucoma-docs.git 48 | GIT_PROGRESS TRUE 49 | GIT_TAG ${FLUID_BRANCH} 50 | ) 51 | 52 | if(FLUID_PATH) 53 | get_filename_component( 54 | FETCHCONTENT_SOURCE_DIR_FLUCOMA-CORE ${FLUID_PATH} ABSOLUTE 55 | ) 56 | endif() 57 | FetchContent_MakeAvailable(flucoma-core) 58 | include(flucoma_version) 59 | include(flucoma-buildtools) 60 | include(flucoma-buildtype) 61 | 62 | set_if_toplevel(VAR FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY 63 | TOPLEVEL "${CMAKE_CURRENT_SOURCE_DIR}/bin" 64 | SUPERBUILD "${CMAKE_SOURCE_DIR}/cli_binaries/${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR}") 65 | 66 | if(DOCS) 67 | if(FLUID_DOCS_PATH) 68 | get_filename_component( 69 | FETCHCONTENT_SOURCE_DIR_FLUCOMA-DOCS ${FLUID_DOCS_PATH} ABSOLUTE 70 | ) 71 | endif() 72 | 73 | FetchContent_MakeAvailable(flucoma-docs) 74 | include(FlucomaDocs) 75 | set(CLI_DOC_OUT "${CMAKE_BINARY_DIR}/cli_ref") 76 | add_ref_target(cli "Making docs") 77 | add_custom_target(CLI_MAKE_DOCS ALL DEPENDS MAKE_CLI_REF) 78 | endif() 79 | 80 | set(LOCAL_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include) 81 | 82 | add_library(FLUID_CLI_WRAPPER INTERFACE) 83 | 84 | target_include_directories (FLUID_CLI_WRAPPER 85 | INTERFACE 86 | "${CMAKE_CURRENT_SOURCE_DIR}/include" 87 | ) 88 | 89 | target_sources(FLUID_CLI_WRAPPER 90 | INTERFACE 91 | "${CMAKE_CURRENT_SOURCE_DIR}/include/FluidCLIWrapper.hpp" 92 | ) 93 | 94 | find_package (Threads REQUIRED) 95 | set(THREADS_PREFER_PTHREAD_FLAG TRUE) 96 | target_link_libraries (FLUID_CLI_WRAPPER INTERFACE Threads::Threads) 97 | 98 | get_client_group(NONE client_list) 99 | list(FILTER client_list INCLUDE REGEX Buf.+) 100 | list(FILTER client_list EXCLUDE REGEX "BufThreadDemo") 101 | list(FILTER client_list EXCLUDE REGEX "BufSelect*") 102 | list(FILTER client_list EXCLUDE REGEX "BufCompose") 103 | list(FILTER client_list EXCLUDE REGEX "BufThresh") 104 | list(FILTER client_list EXCLUDE REGEX "BufScale") 105 | list(FILTER client_list EXCLUDE REGEX "BufFlatten") 106 | 107 | include(MakeCLIStub) 108 | 109 | foreach(client ${client_list}) 110 | get_core_client_header(${client} header) 111 | get_core_client_class(${client} class) 112 | generate_cli_source( 113 | CLIENTS ${client} 114 | HEADERS ${header} 115 | CLASSES ${class} 116 | ) 117 | endforeach() 118 | 119 | #install 120 | set(CLI_INSTALL_PREFIX "." CACHE PATH "Prefix for assembling PD packages") 121 | set(FLUID_PACKAGE_NAME FluidCorpusManipulation CACHE STRING "Name for published package") 122 | set(CLI_PACKAGE_ROOT ${CLI_INSTALL_PREFIX}/${FLUID_PACKAGE_NAME}) 123 | 124 | install(DIRECTORY ${FLUCOMA_CLI_RUNTIME_OUTPUT_DIRECTORY}/ 125 | DESTINATION ${CLI_PACKAGE_ROOT}/bin 126 | USE_SOURCE_PERMISSIONS 127 | PATTERN "*.ilk" EXCLUDE 128 | PATTERN "*.PDB" EXCLUDE) 129 | 130 | install(FILES QuickStart.md DESTINATION ${CLI_PACKAGE_ROOT}) 131 | install(FILES ${flucoma-core_SOURCE_DIR}/distribution.lic 132 | DESTINATION ${CLI_PACKAGE_ROOT} RENAME LICENSE.md) 133 | if(DOCS) 134 | install(DIRECTORY "${FLUID_CLI_REF_PATH}/" 135 | DESTINATION "${CLI_PACKAGE_ROOT}/docs" 136 | FILES_MATCHING REGEX "\\.(html|css)") 137 | endif() 138 | -------------------------------------------------------------------------------- /include/FluidCLIWrapper.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) 3 | Copyright University of Huddersfield. 4 | Licensed under the BSD-3 License. 5 | See license.md file in the project root for full license information. 6 | This project has received funding from the European Research Council (ERC) 7 | under the European Union’s Horizon 2020 research and innovation programme 8 | (grant agreement No 725899). 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | namespace fluid { 34 | namespace client { 35 | 36 | class CLIBufferAdaptor : public BufferAdaptor 37 | { 38 | 39 | public: 40 | CLIBufferAdaptor(const std::string str) 41 | : mPath(str), mAcquired(false), mNumChans(1) 42 | { 43 | // TODO: only read if needed!... 44 | 45 | htl::in_audio_file file(mPath); 46 | 47 | mReadError = file.error_flags(); 48 | 49 | if (file.is_open()) 50 | { 51 | resize(file.frames(), file.channels(), file.sampling_rate()); 52 | file.seek(); 53 | file.read_interleaved(mData.data(),file.frames()); 54 | } 55 | } 56 | 57 | void write(bool allowCSV, bool& result) 58 | { 59 | if (!numFrames()) return; 60 | 61 | index pathLength = mPath.length(); 62 | 63 | if (allowCSV && (pathLength > 4) && 64 | !mPath.compare(pathLength - 4, 4, ".csv")) 65 | { 66 | FluidTensorView view{mData.data(), 0, numFrames(), 67 | numChans()}; 68 | std::ofstream file(mPath.c_str()); 69 | 70 | if (file.is_open()) 71 | { 72 | file << std::setprecision(std::numeric_limits::max_digits10) 73 | << view.transpose(); 74 | file.close(); 75 | } 76 | else 77 | { 78 | result = false; 79 | std::cerr << "Could not open file " << mPath << " for writing\n"; 80 | } 81 | } 82 | else 83 | { 84 | // TODO: file extensions/paths 85 | 86 | constexpr auto fileType = htl::audio_file_format::file_type::wave; 87 | constexpr auto depthType = htl::audio_file_format::pcm_format::float32; 88 | 89 | uint16_t chans = static_cast( 90 | std::min(asSigned(std::numeric_limits::max()), numChans())); 91 | 92 | htl::out_audio_file file(mPath, fileType, depthType, chans, 93 | mSamplingRate); 94 | 95 | if (file.is_open()) 96 | { 97 | std::cout << "Writing " << mPath << '\n'; 98 | file.seek(); 99 | file.write_interleaved(mData.data(), static_cast(numFrames())); 100 | 101 | if(file.is_error()) 102 | { 103 | result = false; 104 | for(auto&& e:file.get_errors()) std::cerr << file.error_string(e) << '\n'; 105 | } 106 | } 107 | else 108 | { 109 | result = false; 110 | std::cerr << "Could not open file " << mPath << " for writing\n"; 111 | } 112 | } 113 | } 114 | 115 | int readError() const { return mReadError; } 116 | 117 | private: 118 | bool acquire() const override { return !mAcquired && (mAcquired = true); } 119 | void release() const override { mAcquired = false; } 120 | 121 | bool valid() const override { return numFrames(); } 122 | bool exists() const override { return true; } 123 | 124 | double sampleRate() const override { return mSamplingRate; } 125 | 126 | const float* getChannel(index channel) const 127 | { 128 | return mData.data() + numFrames() * channel; 129 | } 130 | float* getChannel(index channel) 131 | { 132 | return mData.data() + numFrames() * channel; 133 | } 134 | 135 | const Result resize(index frames, index channels, double sampleRate) override 136 | { 137 | mNumChans = channels; 138 | mData.resize(frames,channels); 139 | mSamplingRate = sampleRate; 140 | return {}; 141 | } 142 | 143 | fluid::FluidTensorView allFrames() override 144 | { 145 | return mData; 146 | } 147 | 148 | fluid::FluidTensorView allFrames() const override 149 | { 150 | return mData; 151 | } 152 | 153 | fluid::FluidTensorView samps(index channel) override 154 | { 155 | return mData.col(channel); 156 | } 157 | 158 | fluid::FluidTensorView samps(index offset, index nframes, 159 | index chanoffset) override 160 | { 161 | return mData(Slice(offset, nframes), Slice(chanoffset, 1)).col(0); 162 | } 163 | 164 | fluid::FluidTensorView samps(index channel) const override 165 | { 166 | return mData.col(channel); 167 | } 168 | 169 | fluid::FluidTensorView samps(index offset, index nframes, 170 | index chanoffset) const override 171 | { 172 | return mData(Slice(offset, nframes), Slice(chanoffset, 1)).col(0); 173 | } 174 | 175 | index numFrames() const override { return mData.rows(); } 176 | index numChans() const override { return mData.cols(); } 177 | 178 | std::string asString() const override { return mPath; } 179 | 180 | std::string mPath; 181 | mutable bool mAcquired; 182 | double mSamplingRate = 44100.0; 183 | index mNumChans; 184 | FluidTensor mData; 185 | int mReadError; 186 | }; 187 | 188 | template 189 | class CLIWrapper 190 | { 191 | enum ErrorType { 192 | kErrNone, 193 | kErrNoOption, 194 | kErrUnknownOption, 195 | kErrAlreadySet, 196 | kErrMissingVals, 197 | kErrValType 198 | }; 199 | 200 | using ClientType = Client; 201 | using ParamSetType = typename ClientType::ParamSetType; 202 | using ConstString = const std::string; 203 | 204 | static constexpr auto& descriptors() 205 | { 206 | return ClientType::getParameterDescriptors(); 207 | } 208 | static constexpr index nParams = descriptors().size(); 209 | 210 | template 211 | static constexpr auto paramDescriptor() 212 | { 213 | return descriptors().template get(); 214 | } 215 | 216 | template 217 | static constexpr index paramSize() 218 | { 219 | return paramDescriptor().fixedSize; 220 | } 221 | 222 | template 223 | static std::string optionName() 224 | { 225 | std::string str(paramDescriptor().name); 226 | std::transform(str.begin(), str.end(), str.begin(), 227 | [](unsigned char c) { return std::tolower(c); }); 228 | str.insert(0, "-"); 229 | 230 | return str; 231 | } 232 | 233 | using FlagsType = std::array; 234 | 235 | template 236 | struct ValidateParams 237 | { 238 | bool numeric(ConstString s, bool d = true) 239 | { 240 | return s.find_first_not_of("0123456789.", s[0] == '-', d ? 11 : 10) == 241 | std::string::npos; 242 | }; 243 | 244 | bool testString(ConstString s, LongT::type) { return numeric(s, false); } 245 | bool testString(ConstString s, FloatT::type) 246 | { 247 | return numeric(s) && s.find(".") == s.find_last_of("."); 248 | } 249 | bool testString(ConstString s, BufferT::type) { return s[0] != '-'; } 250 | bool testString(ConstString s, InputBufferT::type) { return s[0] != '-'; } 251 | bool testString(ConstString s, ChoicesT::type) { return s[0] != '-'; } 252 | 253 | template 254 | ErrorType checkValues(index& i, index argc, const char* argv[], index nArgs, 255 | T) 256 | { 257 | if (!(i + nArgs < argc)) return kErrMissingVals; 258 | 259 | for (index j = 1; j <= nArgs; j++) 260 | if (!testString(argv[i + j], T())) return kErrValType; 261 | 262 | i += 1 + nArgs; 263 | return kErrNone; 264 | } 265 | 266 | ErrorType operator()(index& i, index argc, const char* argv[], 267 | FlagsType& flags) 268 | { 269 | using T = typename ClientType::ParamDescType::template ParamType; 270 | using ArgType = 271 | typename ParamLiteralConvertor()>::LiteralType; 272 | 273 | if (argv[i][0] != '-' || numeric(argv[i])) return kErrNoOption; 274 | 275 | if (!strcmp(argv[i], optionName().c_str())) 276 | { 277 | if (flags[N]) return kErrAlreadySet; 278 | flags[N] = true; 279 | return checkValues(i, argc, argv, paramSize(), ArgType()); 280 | } 281 | 282 | return ValidateParams()(i, argc, argv, flags); 283 | } 284 | }; 285 | 286 | template 287 | struct ValidateParams 288 | { 289 | ErrorType operator()(index&, index, const char*[], FlagsType&) 290 | { 291 | return kErrUnknownOption; 292 | } 293 | }; 294 | 295 | template 296 | struct Setter 297 | { 298 | auto fromString(ConstString s, LongT::type) { return std::stol(s); } 299 | auto fromString(ConstString s, FloatT::type) { return std::stod(s); } 300 | auto fromString(ConstString s, BufferT::type) 301 | { 302 | return BufferT::type(new CLIBufferAdaptor(s)); 303 | } 304 | auto fromString(ConstString s, InputBufferT::type) 305 | { 306 | return InputBufferT::type(new CLIBufferAdaptor(s)); 307 | } 308 | 309 | typename T::type operator()(index argc, const char* argv[]) 310 | { 311 | for (index i = 0; i < argc; ++i) 312 | { 313 | if (!strcmp(argv[i], optionName().c_str())) 314 | { 315 | ParamLiteralConvertor()> a; 316 | 317 | for (index j = 0; j < paramSize(); ++j) 318 | a[j] = fromString(argv[i + j + 1], a[0]); 319 | 320 | return a.value(); 321 | } 322 | } 323 | 324 | return paramDescriptor().defaultValue; 325 | } 326 | 327 | auto fromString(ConstString s,ChoicesT::type) 328 | { 329 | // vector choices; 330 | std::istringstream c(s); 331 | c >> std::ws; 332 | std::string tmp; 333 | size_t choices{0}; 334 | auto desc = paramDescriptor(); 335 | while(std::getline(c,tmp,' ')) 336 | { 337 | index idx = desc.lookup(tmp); 338 | 339 | if( idx > -1) 340 | { 341 | choices = choices | 1 << idx; 342 | continue; 343 | } 344 | std::cerr << "Warning: unrecognised option " << tmp << std::endl; 345 | } 346 | 347 | return ChoicesT::type(choices); 348 | 349 | } 350 | 351 | }; 352 | 353 | template 354 | struct Help 355 | { 356 | void operator()(T descriptor) 357 | { 358 | std::cout << std::setw(25) << std::setfill(' '); 359 | std::cout.unsetf(std::ios::right); 360 | std::cout.setf(std::ios::left); 361 | std::cout << optionName(); 362 | std::cout.unsetf(std::ios::left); 363 | std::cout.setf(std::ios::right); 364 | std::cout.unsetf(std::ios::right); 365 | std::cout << descriptor.displayName << "\n"; 366 | } 367 | }; 368 | 369 | template 370 | struct WriteFiles 371 | { 372 | void operator()(typename T::type& param, bool allowCSV, bool& result) 373 | { 374 | if (param) static_cast(param.get())->write(allowCSV, result); 375 | } 376 | }; 377 | 378 | template 379 | struct CheckRead 380 | { 381 | void operator()(typename T::type& param, bool& result) 382 | { 383 | if(param) 384 | { 385 | const CLIBufferAdaptor* ifile = static_cast(param.get()); 386 | if(ifile->readError()) 387 | { 388 | std::cout << ifile->readError() << '\n'; 389 | using AudioFile = htl::base_audio_file; 390 | result = false; 391 | std::vector errors = AudioFile::extract_errors_from_flags(ifile->readError()); 392 | for(auto&& e:errors) std::cerr << AudioFile::error_string(e) << '\n'; 393 | } 394 | } 395 | } 396 | }; 397 | 398 | 399 | public: 400 | static void report(const char* str1, const char* str2) 401 | { 402 | std::cout << str1 << " " << str2 << "\n"; 403 | } 404 | 405 | static int run(index argc, const char* argv[]) 406 | { 407 | ParamSetType params(descriptors(), FluidDefaultAllocator()); 408 | FlagsType flags; 409 | flags.fill(false); 410 | 411 | const std::regex help("(-*)h(elp)?"); 412 | const std::regex version("(-*)v(ersion)?"); 413 | std::cmatch m; 414 | if (argc > 1 && std::regex_match(argv[1], m, help)) 415 | { 416 | std::cout << "Fluid Corpus Manipulation Toolkit, version " 417 | << fluidVersion() << '\n'; 418 | std::cout << "Part of the Fluid Corpus Manipulation Project - " 419 | "http:://www.flucoma.org/\n"; 420 | std::cout 421 | << "For a more detailed description of the available options than " 422 | "given below, please see the accompanying HTML documentation.\n"; 423 | std::cout << "Call with these options:\n"; 424 | descriptors().template iterate(); 425 | return 0; 426 | } 427 | 428 | if (argc > 1 && std::regex_match(argv[1], m, version)) 429 | { 430 | std::cout << "Fluid Corpus Manipulation Toolkit, version " 431 | << fluidVersion() << '\n'; 432 | return 0; 433 | } 434 | 435 | for (index i = 1; i < argc;) 436 | { 437 | switch (ValidateParams()(i, argc, argv, flags)) 438 | { 439 | case kErrNone: break; 440 | case kErrNoOption: 441 | report("Expected option, but found", argv[i]); 442 | return -2; 443 | case kErrUnknownOption: report("Unknown option", argv[i]); return -3; 444 | case kErrAlreadySet: 445 | report("More than one use of option", argv[i]); 446 | return -4; 447 | case kErrMissingVals: 448 | report("Missing values for option", argv[i]); 449 | return -5; 450 | case kErrValType: 451 | report("Values wrong type for option", argv[i]); 452 | return -6; 453 | } 454 | } 455 | 456 | params.template setParameterValues(false, argc, argv); 457 | params.constrainParameterValues(); 458 | 459 | bool readSuccess{true}; 460 | params.template forEachParamType(readSuccess); 461 | if(!readSuccess) return -1; 462 | 463 | // Create client after all parameters are set 464 | 465 | ClientType client(params, FluidContext()); 466 | Result result; 467 | 468 | client.enqueue(params); 469 | result = client.process(); 470 | 471 | double progress = 0.0; 472 | 473 | while(result.ok()) 474 | { 475 | ProcessState state = client.checkProgress(result); 476 | 477 | if (state == ProcessState::kDone || state == ProcessState::kDoneStillProcessing) { 478 | std::cout << "100%\n"; 479 | break; 480 | } 481 | if (state != ProcessState::kDone) { 482 | double newProgress = client.progress(); 483 | if (newProgress - progress >=0.01) 484 | { 485 | std::cout << std::setw(3) << static_cast(100 * newProgress) << "%\r" << std::flush; 486 | progress = newProgress; 487 | } 488 | using namespace std::chrono_literals; 489 | std::this_thread::sleep_for(20ms); 490 | continue; 491 | } 492 | } 493 | 494 | if (!result.ok()) 495 | { 496 | // Output error 497 | 498 | std::cerr << result.message() << "\n"; 499 | } 500 | else 501 | { 502 | // Write files 503 | 504 | bool allowCSV = true; 505 | bool fileWriteResult = true; 506 | params.template forEachParamType(allowCSV, fileWriteResult); 507 | if(!fileWriteResult) return -1; 508 | } 509 | 510 | return result.ok() ? 0 : -1; 511 | } 512 | }; 513 | 514 | } // namespace client 515 | } // namespace fluid 516 | -------------------------------------------------------------------------------- /testing-code/slice-reader.maxpat: -------------------------------------------------------------------------------- 1 | { 2 | "patcher" : { 3 | "fileversion" : 1, 4 | "appversion" : { 5 | "major" : 7, 6 | "minor" : 3, 7 | "revision" : 5, 8 | "architecture" : "x64", 9 | "modernui" : 1 10 | } 11 | , 12 | "rect" : [ 34.0, 79.0, 1109.0, 990.0 ], 13 | "bglocked" : 0, 14 | "openinpresentation" : 0, 15 | "default_fontsize" : 12.0, 16 | "default_fontface" : 0, 17 | "default_fontname" : "Arial", 18 | "gridonopen" : 1, 19 | "gridsize" : [ 15.0, 15.0 ], 20 | "gridsnaponopen" : 1, 21 | "objectsnaponopen" : 1, 22 | "statusbarvisible" : 2, 23 | "toolbarvisible" : 1, 24 | "lefttoolbarpinned" : 0, 25 | "toptoolbarpinned" : 0, 26 | "righttoolbarpinned" : 0, 27 | "bottomtoolbarpinned" : 0, 28 | "toolbars_unpinned_last_save" : 0, 29 | "tallnewobj" : 0, 30 | "boxanimatetime" : 200, 31 | "enablehscroll" : 1, 32 | "enablevscroll" : 1, 33 | "devicewidth" : 0.0, 34 | "description" : "", 35 | "digest" : "", 36 | "tags" : "", 37 | "style" : "", 38 | "subpatcher_template" : "", 39 | "boxes" : [ { 40 | "box" : { 41 | "id" : "obj-21", 42 | "maxclass" : "message", 43 | "numinlets" : 2, 44 | "numoutlets" : 1, 45 | "outlettype" : [ "" ], 46 | "patching_rect" : [ 388.0, 164.0, 50.0, 22.0 ], 47 | "style" : "", 48 | "text" : "replace" 49 | } 50 | 51 | } 52 | , { 53 | "box" : { 54 | "id" : "obj-42", 55 | "maxclass" : "newobj", 56 | "numinlets" : 2, 57 | "numoutlets" : 3, 58 | "outlettype" : [ "bang", "bang", "int" ], 59 | "patching_rect" : [ 769.0, 863.0, 46.0, 22.0 ], 60 | "style" : "", 61 | "text" : "uzi 0 0" 62 | } 63 | 64 | } 65 | , { 66 | "box" : { 67 | "id" : "obj-40", 68 | "maxclass" : "newobj", 69 | "numinlets" : 1, 70 | "numoutlets" : 0, 71 | "patching_rect" : [ 801.0, 922.5, 34.0, 22.0 ], 72 | "style" : "", 73 | "text" : "print" 74 | } 75 | 76 | } 77 | , { 78 | "box" : { 79 | "id" : "obj-41", 80 | "maxclass" : "newobj", 81 | "numinlets" : 3, 82 | "numoutlets" : 1, 83 | "outlettype" : [ "float" ], 84 | "patching_rect" : [ 801.0, 887.0, 85.0, 22.0 ], 85 | "style" : "", 86 | "text" : "peek~ maxidx" 87 | } 88 | 89 | } 90 | , { 91 | "box" : { 92 | "id" : "obj-39", 93 | "maxclass" : "button", 94 | "numinlets" : 1, 95 | "numoutlets" : 1, 96 | "outlettype" : [ "bang" ], 97 | "patching_rect" : [ 661.0, 206.0, 24.0, 24.0 ], 98 | "style" : "" 99 | } 100 | 101 | } 102 | , { 103 | "box" : { 104 | "id" : "obj-37", 105 | "maxclass" : "newobj", 106 | "numinlets" : 1, 107 | "numoutlets" : 0, 108 | "patching_rect" : [ 866.0, 404.5, 34.0, 22.0 ], 109 | "style" : "", 110 | "text" : "print" 111 | } 112 | 113 | } 114 | , { 115 | "box" : { 116 | "id" : "obj-36", 117 | "maxclass" : "newobj", 118 | "numinlets" : 3, 119 | "numoutlets" : 1, 120 | "outlettype" : [ "float" ], 121 | "patching_rect" : [ 866.0, 369.0, 84.0, 22.0 ], 122 | "style" : "", 123 | "text" : "peek~ indices" 124 | } 125 | 126 | } 127 | , { 128 | "box" : { 129 | "id" : "obj-35", 130 | "maxclass" : "newobj", 131 | "numinlets" : 2, 132 | "numoutlets" : 3, 133 | "outlettype" : [ "bang", "bang", "int" ], 134 | "patching_rect" : [ 839.0, 333.0, 46.0, 22.0 ], 135 | "style" : "", 136 | "text" : "uzi 0 0" 137 | } 138 | 139 | } 140 | , { 141 | "box" : { 142 | "id" : "obj-33", 143 | "maxclass" : "newobj", 144 | "numinlets" : 1, 145 | "numoutlets" : 2, 146 | "outlettype" : [ "float", "bang" ], 147 | "patching_rect" : [ 515.0, 699.0, 89.0, 22.0 ], 148 | "style" : "", 149 | "text" : "buffer~ maxidx" 150 | } 151 | 152 | } 153 | , { 154 | "box" : { 155 | "id" : "obj-31", 156 | "maxclass" : "button", 157 | "numinlets" : 1, 158 | "numoutlets" : 1, 159 | "outlettype" : [ "bang" ], 160 | "patching_rect" : [ 94.0, 898.0, 24.0, 24.0 ], 161 | "style" : "" 162 | } 163 | 164 | } 165 | , { 166 | "box" : { 167 | "id" : "obj-32", 168 | "maxclass" : "newobj", 169 | "numinlets" : 1, 170 | "numoutlets" : 2, 171 | "outlettype" : [ "bang", "" ], 172 | "patching_rect" : [ 81.0, 944.0, 287.0, 22.0 ], 173 | "style" : "", 174 | "text" : "fluid.bufnoveltyslice~ @source src @indices maxidx" 175 | } 176 | 177 | } 178 | , { 179 | "box" : { 180 | "id" : "obj-30", 181 | "maxclass" : "button", 182 | "numinlets" : 1, 183 | "numoutlets" : 1, 184 | "outlettype" : [ "bang" ], 185 | "patching_rect" : [ 652.0, 760.0, 24.0, 24.0 ], 186 | "style" : "" 187 | } 188 | 189 | } 190 | , { 191 | "box" : { 192 | "id" : "obj-23", 193 | "maxclass" : "button", 194 | "numinlets" : 1, 195 | "numoutlets" : 1, 196 | "outlettype" : [ "bang" ], 197 | "patching_rect" : [ 150.0, 691.0, 24.0, 24.0 ], 198 | "style" : "" 199 | } 200 | 201 | } 202 | , { 203 | "box" : { 204 | "id" : "obj-15", 205 | "maxclass" : "number", 206 | "numinlets" : 1, 207 | "numoutlets" : 2, 208 | "outlettype" : [ "", "bang" ], 209 | "parameter_enable" : 0, 210 | "patching_rect" : [ 617.0, 866.5, 98.0, 22.0 ], 211 | "style" : "" 212 | } 213 | 214 | } 215 | , { 216 | "box" : { 217 | "id" : "obj-18", 218 | "maxclass" : "newobj", 219 | "numinlets" : 2, 220 | "numoutlets" : 1, 221 | "outlettype" : [ "" ], 222 | "patching_rect" : [ 617.0, 833.5, 181.0, 22.0 ], 223 | "style" : "", 224 | "text" : "expr int(($f1 / 1000. * $f2) + 0.5)" 225 | } 226 | 227 | } 228 | , { 229 | "box" : { 230 | "id" : "obj-19", 231 | "maxclass" : "newobj", 232 | "numinlets" : 1, 233 | "numoutlets" : 9, 234 | "outlettype" : [ "float", "list", "float", "float", "float", "float", "float", "", "int" ], 235 | "patching_rect" : [ 617.0, 806.0, 103.0, 22.0 ], 236 | "style" : "", 237 | "text" : "info~ maxidx" 238 | } 239 | 240 | } 241 | , { 242 | "box" : { 243 | "id" : "obj-13", 244 | "maxclass" : "newobj", 245 | "numinlets" : 1, 246 | "numoutlets" : 2, 247 | "outlettype" : [ "bang", "" ], 248 | "patching_rect" : [ 137.0, 737.0, 346.0, 22.0 ], 249 | "style" : "", 250 | "text" : "fluid.bufonsetslice~ @source src @indices maxidx @function 6" 251 | } 252 | 253 | } 254 | , { 255 | "box" : { 256 | "id" : "obj-14", 257 | "maxclass" : "number", 258 | "numinlets" : 1, 259 | "numoutlets" : 2, 260 | "outlettype" : [ "", "bang" ], 261 | "parameter_enable" : 0, 262 | "patching_rect" : [ 613.0, 394.0, 50.0, 22.0 ], 263 | "style" : "" 264 | } 265 | 266 | } 267 | , { 268 | "box" : { 269 | "id" : "obj-3", 270 | "maxclass" : "number", 271 | "numinlets" : 1, 272 | "numoutlets" : 2, 273 | "outlettype" : [ "", "bang" ], 274 | "parameter_enable" : 0, 275 | "patching_rect" : [ 681.0, 595.5, 98.0, 22.0 ], 276 | "style" : "" 277 | } 278 | 279 | } 280 | , { 281 | "box" : { 282 | "id" : "obj-9", 283 | "maxclass" : "newobj", 284 | "numinlets" : 3, 285 | "numoutlets" : 1, 286 | "outlettype" : [ "float" ], 287 | "patching_rect" : [ 681.0, 567.5, 100.0, 22.0 ], 288 | "style" : "", 289 | "text" : "peek~ slicestarts" 290 | } 291 | 292 | } 293 | , { 294 | "box" : { 295 | "id" : "obj-10", 296 | "maxclass" : "number", 297 | "numinlets" : 1, 298 | "numoutlets" : 2, 299 | "outlettype" : [ "", "bang" ], 300 | "parameter_enable" : 0, 301 | "patching_rect" : [ 681.0, 540.5, 50.0, 22.0 ], 302 | "style" : "" 303 | } 304 | 305 | } 306 | , { 307 | "box" : { 308 | "id" : "obj-17", 309 | "maxclass" : "number", 310 | "numinlets" : 1, 311 | "numoutlets" : 2, 312 | "outlettype" : [ "", "bang" ], 313 | "parameter_enable" : 0, 314 | "patching_rect" : [ 605.0, 301.5, 98.0, 22.0 ], 315 | "style" : "" 316 | } 317 | 318 | } 319 | , { 320 | "box" : { 321 | "id" : "obj-16", 322 | "maxclass" : "newobj", 323 | "numinlets" : 2, 324 | "numoutlets" : 1, 325 | "outlettype" : [ "" ], 326 | "patching_rect" : [ 605.0, 268.5, 181.0, 22.0 ], 327 | "style" : "", 328 | "text" : "expr int(($f1 / 1000. * $f2) + 0.5)" 329 | } 330 | 331 | } 332 | , { 333 | "box" : { 334 | "id" : "obj-12", 335 | "maxclass" : "newobj", 336 | "numinlets" : 1, 337 | "numoutlets" : 9, 338 | "outlettype" : [ "float", "list", "float", "float", "float", "float", "float", "", "int" ], 339 | "patching_rect" : [ 605.0, 241.0, 103.0, 22.0 ], 340 | "style" : "", 341 | "text" : "info~ indices" 342 | } 343 | 344 | } 345 | , { 346 | "box" : { 347 | "id" : "obj-8", 348 | "maxclass" : "number", 349 | "numinlets" : 1, 350 | "numoutlets" : 2, 351 | "outlettype" : [ "", "bang" ], 352 | "parameter_enable" : 0, 353 | "patching_rect" : [ 681.0, 479.5, 98.0, 22.0 ], 354 | "style" : "" 355 | } 356 | 357 | } 358 | , { 359 | "box" : { 360 | "id" : "obj-7", 361 | "maxclass" : "newobj", 362 | "numinlets" : 3, 363 | "numoutlets" : 1, 364 | "outlettype" : [ "float" ], 365 | "patching_rect" : [ 681.0, 451.5, 84.0, 22.0 ], 366 | "style" : "", 367 | "text" : "peek~ indices" 368 | } 369 | 370 | } 371 | , { 372 | "box" : { 373 | "id" : "obj-6", 374 | "maxclass" : "number", 375 | "numinlets" : 1, 376 | "numoutlets" : 2, 377 | "outlettype" : [ "", "bang" ], 378 | "parameter_enable" : 0, 379 | "patching_rect" : [ 681.0, 424.5, 50.0, 22.0 ], 380 | "style" : "" 381 | } 382 | 383 | } 384 | , { 385 | "box" : { 386 | "id" : "obj-5", 387 | "maxclass" : "message", 388 | "numinlets" : 2, 389 | "numoutlets" : 1, 390 | "outlettype" : [ "" ], 391 | "patching_rect" : [ 535.0, 172.0, 50.0, 22.0 ], 392 | "style" : "", 393 | "text" : "replace" 394 | } 395 | 396 | } 397 | , { 398 | "box" : { 399 | "id" : "obj-4", 400 | "maxclass" : "message", 401 | "numinlets" : 2, 402 | "numoutlets" : 1, 403 | "outlettype" : [ "" ], 404 | "patching_rect" : [ 449.0, 100.0, 255.0, 22.0 ], 405 | "style" : "", 406 | "text" : "replace Tremblay-AaS-AcousticStrums-M.wav" 407 | } 408 | 409 | } 410 | , { 411 | "box" : { 412 | "id" : "obj-2", 413 | "maxclass" : "newobj", 414 | "numinlets" : 1, 415 | "numoutlets" : 2, 416 | "outlettype" : [ "float", "bang" ], 417 | "patching_rect" : [ 535.0, 206.0, 89.0, 22.0 ], 418 | "style" : "", 419 | "text" : "buffer~ indices" 420 | } 421 | 422 | } 423 | , { 424 | "box" : { 425 | "id" : "obj-1", 426 | "maxclass" : "newobj", 427 | "numinlets" : 1, 428 | "numoutlets" : 2, 429 | "outlettype" : [ "float", "bang" ], 430 | "patching_rect" : [ 449.0, 206.0, 67.0, 22.0 ], 431 | "style" : "", 432 | "text" : "buffer~ src" 433 | } 434 | 435 | } 436 | , { 437 | "box" : { 438 | "id" : "obj-70", 439 | "maxclass" : "comment", 440 | "numinlets" : 1, 441 | "numoutlets" : 0, 442 | "patching_rect" : [ 515.0, 383.5, 33.0, 20.0 ], 443 | "style" : "", 444 | "text" : "play" 445 | } 446 | 447 | } 448 | , { 449 | "box" : { 450 | "bgcolor" : [ 1.0, 0.788235, 0.470588, 1.0 ], 451 | "fontname" : "Arial Bold", 452 | "hint" : "", 453 | "id" : "obj-63", 454 | "ignoreclick" : 1, 455 | "legacytextcolor" : 1, 456 | "maxclass" : "textbutton", 457 | "numinlets" : 1, 458 | "numoutlets" : 3, 459 | "outlettype" : [ "", "", "int" ], 460 | "parameter_enable" : 0, 461 | "patching_rect" : [ 485.0, 380.5, 20.0, 20.0 ], 462 | "presentation_rect" : [ 1264.0, 527.067627, 20.0, 20.0 ], 463 | "rounded" : 60.0, 464 | "style" : "", 465 | "text" : "6", 466 | "textcolor" : [ 0.34902, 0.34902, 0.34902, 1.0 ] 467 | } 468 | 469 | } 470 | , { 471 | "box" : { 472 | "id" : "obj-56", 473 | "maxclass" : "comment", 474 | "numinlets" : 1, 475 | "numoutlets" : 0, 476 | "patching_rect" : [ 227.0, 241.0, 79.0, 20.0 ], 477 | "style" : "", 478 | "text" : "select a slice" 479 | } 480 | 481 | } 482 | , { 483 | "box" : { 484 | "bgcolor" : [ 1.0, 0.788235, 0.470588, 1.0 ], 485 | "fontname" : "Arial Bold", 486 | "hint" : "", 487 | "id" : "obj-24", 488 | "ignoreclick" : 1, 489 | "legacytextcolor" : 1, 490 | "maxclass" : "textbutton", 491 | "numinlets" : 1, 492 | "numoutlets" : 3, 493 | "outlettype" : [ "", "", "int" ], 494 | "parameter_enable" : 0, 495 | "patching_rect" : [ 205.0, 241.0, 20.0, 20.0 ], 496 | "presentation_rect" : [ 1249.0, 483.067627, 20.0, 20.0 ], 497 | "rounded" : 60.0, 498 | "style" : "", 499 | "text" : "5", 500 | "textcolor" : [ 0.34902, 0.34902, 0.34902, 1.0 ] 501 | } 502 | 503 | } 504 | , { 505 | "box" : { 506 | "id" : "obj-11", 507 | "maxclass" : "message", 508 | "numinlets" : 2, 509 | "numoutlets" : 1, 510 | "outlettype" : [ "" ], 511 | "patching_rect" : [ 90.0, 494.5, 140.0, 22.0 ], 512 | "style" : "", 513 | "text" : "374.920635" 514 | } 515 | 516 | } 517 | , { 518 | "box" : { 519 | "id" : "obj-72", 520 | "maxclass" : "newobj", 521 | "numinlets" : 2, 522 | "numoutlets" : 2, 523 | "outlettype" : [ "bang", "" ], 524 | "patching_rect" : [ 399.5, 466.5, 36.0, 22.0 ], 525 | "style" : "", 526 | "text" : "sel 0" 527 | } 528 | 529 | } 530 | , { 531 | "box" : { 532 | "id" : "obj-69", 533 | "maxclass" : "newobj", 534 | "numinlets" : 1, 535 | "numoutlets" : 2, 536 | "outlettype" : [ "signal", "float" ], 537 | "patching_rect" : [ 231.0, 380.5, 79.0, 22.0 ], 538 | "style" : "", 539 | "text" : "sampstoms~" 540 | } 541 | 542 | } 543 | , { 544 | "box" : { 545 | "id" : "obj-68", 546 | "maxclass" : "newobj", 547 | "numinlets" : 1, 548 | "numoutlets" : 2, 549 | "outlettype" : [ "", "" ], 550 | "patching_rect" : [ 291.0, 428.5, 58.5, 22.0 ], 551 | "style" : "", 552 | "text" : "t l l" 553 | } 554 | 555 | } 556 | , { 557 | "box" : { 558 | "id" : "obj-66", 559 | "maxclass" : "newobj", 560 | "numinlets" : 1, 561 | "numoutlets" : 1, 562 | "outlettype" : [ "" ], 563 | "patching_rect" : [ 330.5, 520.5, 81.0, 22.0 ], 564 | "style" : "", 565 | "text" : "prepend start" 566 | } 567 | 568 | } 569 | , { 570 | "box" : { 571 | "id" : "obj-65", 572 | "maxclass" : "newobj", 573 | "numinlets" : 2, 574 | "numoutlets" : 1, 575 | "outlettype" : [ "" ], 576 | "patching_rect" : [ 330.5, 490.5, 105.0, 22.0 ], 577 | "style" : "", 578 | "text" : "join 2 @triggers 1" 579 | } 580 | 581 | } 582 | , { 583 | "box" : { 584 | "id" : "obj-58", 585 | "maxclass" : "newobj", 586 | "numinlets" : 2, 587 | "numoutlets" : 1, 588 | "outlettype" : [ "float" ], 589 | "patching_rect" : [ 291.0, 453.5, 31.0, 22.0 ], 590 | "style" : "", 591 | "text" : "!- 0." 592 | } 593 | 594 | } 595 | , { 596 | "box" : { 597 | "id" : "obj-53", 598 | "maxclass" : "newobj", 599 | "numinlets" : 2, 600 | "numoutlets" : 2, 601 | "outlettype" : [ "", "" ], 602 | "patching_rect" : [ 291.0, 404.5, 63.0, 22.0 ], 603 | "style" : "", 604 | "text" : "zl group 2" 605 | } 606 | 607 | } 608 | , { 609 | "box" : { 610 | "id" : "obj-50", 611 | "maxclass" : "newobj", 612 | "numinlets" : 1, 613 | "numoutlets" : 2, 614 | "outlettype" : [ "int", "int" ], 615 | "patching_rect" : [ 205.0, 301.5, 45.0, 22.0 ], 616 | "style" : "", 617 | "text" : "t i i" 618 | } 619 | 620 | } 621 | , { 622 | "box" : { 623 | "id" : "obj-49", 624 | "maxclass" : "number", 625 | "numinlets" : 1, 626 | "numoutlets" : 2, 627 | "outlettype" : [ "", "bang" ], 628 | "parameter_enable" : 0, 629 | "patching_rect" : [ 205.0, 268.5, 50.0, 22.0 ], 630 | "style" : "" 631 | } 632 | 633 | } 634 | , { 635 | "box" : { 636 | "id" : "obj-47", 637 | "maxclass" : "newobj", 638 | "numinlets" : 2, 639 | "numoutlets" : 1, 640 | "outlettype" : [ "int" ], 641 | "patching_rect" : [ 205.0, 325.5, 29.5, 22.0 ], 642 | "style" : "", 643 | "text" : "+ 1" 644 | } 645 | 646 | } 647 | , { 648 | "box" : { 649 | "id" : "obj-43", 650 | "maxclass" : "newobj", 651 | "numinlets" : 3, 652 | "numoutlets" : 1, 653 | "outlettype" : [ "float" ], 654 | "patching_rect" : [ 231.0, 356.5, 84.0, 22.0 ], 655 | "style" : "", 656 | "text" : "peek~ indices" 657 | } 658 | 659 | } 660 | , { 661 | "box" : { 662 | "id" : "obj-26", 663 | "maxclass" : "message", 664 | "numinlets" : 2, 665 | "numoutlets" : 1, 666 | "outlettype" : [ "" ], 667 | "patching_rect" : [ 526.0, 411.5, 33.0, 22.0 ], 668 | "style" : "", 669 | "text" : "stop" 670 | } 671 | 672 | } 673 | , { 674 | "box" : { 675 | "id" : "obj-27", 676 | "maxclass" : "message", 677 | "numinlets" : 2, 678 | "numoutlets" : 1, 679 | "outlettype" : [ "" ], 680 | "patching_rect" : [ 491.0, 411.5, 34.0, 22.0 ], 681 | "style" : "", 682 | "text" : "start" 683 | } 684 | 685 | } 686 | , { 687 | "box" : { 688 | "id" : "obj-28", 689 | "maxclass" : "newobj", 690 | "numinlets" : 2, 691 | "numoutlets" : 0, 692 | "patching_rect" : [ 491.0, 502.5, 37.0, 22.0 ], 693 | "style" : "", 694 | "text" : "dac~" 695 | } 696 | 697 | } 698 | , { 699 | "box" : { 700 | "id" : "obj-29", 701 | "maxclass" : "newobj", 702 | "numinlets" : 1, 703 | "numoutlets" : 3, 704 | "outlettype" : [ "signal", "signal", "bang" ], 705 | "patching_rect" : [ 491.0, 453.5, 69.0, 22.0 ], 706 | "style" : "", 707 | "text" : "play~ src 2" 708 | } 709 | 710 | } 711 | ], 712 | "lines" : [ { 713 | "patchline" : { 714 | "destination" : [ "obj-9", 0 ], 715 | "source" : [ "obj-10", 0 ] 716 | } 717 | 718 | } 719 | , { 720 | "patchline" : { 721 | "destination" : [ "obj-16", 1 ], 722 | "source" : [ "obj-12", 6 ] 723 | } 724 | 725 | } 726 | , { 727 | "patchline" : { 728 | "destination" : [ "obj-16", 0 ], 729 | "source" : [ "obj-12", 0 ] 730 | } 731 | 732 | } 733 | , { 734 | "patchline" : { 735 | "destination" : [ "obj-10", 0 ], 736 | "order" : 0, 737 | "source" : [ "obj-14", 0 ] 738 | } 739 | 740 | } 741 | , { 742 | "patchline" : { 743 | "destination" : [ "obj-6", 0 ], 744 | "order" : 1, 745 | "source" : [ "obj-14", 0 ] 746 | } 747 | 748 | } 749 | , { 750 | "patchline" : { 751 | "destination" : [ "obj-17", 0 ], 752 | "order" : 1, 753 | "source" : [ "obj-16", 0 ] 754 | } 755 | 756 | } 757 | , { 758 | "patchline" : { 759 | "destination" : [ "obj-35", 0 ], 760 | "order" : 0, 761 | "source" : [ "obj-16", 0 ] 762 | } 763 | 764 | } 765 | , { 766 | "patchline" : { 767 | "destination" : [ "obj-15", 0 ], 768 | "order" : 1, 769 | "source" : [ "obj-18", 0 ] 770 | } 771 | 772 | } 773 | , { 774 | "patchline" : { 775 | "destination" : [ "obj-42", 0 ], 776 | "order" : 0, 777 | "source" : [ "obj-18", 0 ] 778 | } 779 | 780 | } 781 | , { 782 | "patchline" : { 783 | "destination" : [ "obj-18", 1 ], 784 | "source" : [ "obj-19", 6 ] 785 | } 786 | 787 | } 788 | , { 789 | "patchline" : { 790 | "destination" : [ "obj-18", 0 ], 791 | "source" : [ "obj-19", 0 ] 792 | } 793 | 794 | } 795 | , { 796 | "patchline" : { 797 | "destination" : [ "obj-12", 0 ], 798 | "source" : [ "obj-2", 1 ] 799 | } 800 | 801 | } 802 | , { 803 | "patchline" : { 804 | "destination" : [ "obj-1", 0 ], 805 | "source" : [ "obj-21", 0 ] 806 | } 807 | 808 | } 809 | , { 810 | "patchline" : { 811 | "destination" : [ "obj-13", 0 ], 812 | "source" : [ "obj-23", 0 ] 813 | } 814 | 815 | } 816 | , { 817 | "patchline" : { 818 | "destination" : [ "obj-29", 0 ], 819 | "source" : [ "obj-26", 0 ] 820 | } 821 | 822 | } 823 | , { 824 | "patchline" : { 825 | "destination" : [ "obj-29", 0 ], 826 | "source" : [ "obj-27", 0 ] 827 | } 828 | 829 | } 830 | , { 831 | "patchline" : { 832 | "destination" : [ "obj-28", 1 ], 833 | "source" : [ "obj-29", 1 ] 834 | } 835 | 836 | } 837 | , { 838 | "patchline" : { 839 | "destination" : [ "obj-28", 0 ], 840 | "source" : [ "obj-29", 0 ] 841 | } 842 | 843 | } 844 | , { 845 | "patchline" : { 846 | "destination" : [ "obj-19", 0 ], 847 | "source" : [ "obj-30", 0 ] 848 | } 849 | 850 | } 851 | , { 852 | "patchline" : { 853 | "destination" : [ "obj-32", 0 ], 854 | "source" : [ "obj-31", 0 ] 855 | } 856 | 857 | } 858 | , { 859 | "patchline" : { 860 | "destination" : [ "obj-36", 0 ], 861 | "source" : [ "obj-35", 2 ] 862 | } 863 | 864 | } 865 | , { 866 | "patchline" : { 867 | "destination" : [ "obj-37", 0 ], 868 | "source" : [ "obj-36", 0 ] 869 | } 870 | 871 | } 872 | , { 873 | "patchline" : { 874 | "destination" : [ "obj-12", 0 ], 875 | "source" : [ "obj-39", 0 ] 876 | } 877 | 878 | } 879 | , { 880 | "patchline" : { 881 | "destination" : [ "obj-1", 0 ], 882 | "source" : [ "obj-4", 0 ] 883 | } 884 | 885 | } 886 | , { 887 | "patchline" : { 888 | "destination" : [ "obj-40", 0 ], 889 | "source" : [ "obj-41", 0 ] 890 | } 891 | 892 | } 893 | , { 894 | "patchline" : { 895 | "destination" : [ "obj-41", 0 ], 896 | "source" : [ "obj-42", 2 ] 897 | } 898 | 899 | } 900 | , { 901 | "patchline" : { 902 | "destination" : [ "obj-69", 0 ], 903 | "source" : [ "obj-43", 0 ] 904 | } 905 | 906 | } 907 | , { 908 | "patchline" : { 909 | "destination" : [ "obj-43", 0 ], 910 | "source" : [ "obj-47", 0 ] 911 | } 912 | 913 | } 914 | , { 915 | "patchline" : { 916 | "destination" : [ "obj-50", 0 ], 917 | "source" : [ "obj-49", 0 ] 918 | } 919 | 920 | } 921 | , { 922 | "patchline" : { 923 | "destination" : [ "obj-2", 0 ], 924 | "source" : [ "obj-5", 0 ] 925 | } 926 | 927 | } 928 | , { 929 | "patchline" : { 930 | "destination" : [ "obj-43", 0 ], 931 | "source" : [ "obj-50", 1 ] 932 | } 933 | 934 | } 935 | , { 936 | "patchline" : { 937 | "destination" : [ "obj-47", 0 ], 938 | "source" : [ "obj-50", 0 ] 939 | } 940 | 941 | } 942 | , { 943 | "patchline" : { 944 | "destination" : [ "obj-68", 0 ], 945 | "source" : [ "obj-53", 0 ] 946 | } 947 | 948 | } 949 | , { 950 | "patchline" : { 951 | "destination" : [ "obj-11", 1 ], 952 | "order" : 1, 953 | "source" : [ "obj-58", 0 ] 954 | } 955 | 956 | } 957 | , { 958 | "patchline" : { 959 | "destination" : [ "obj-72", 0 ], 960 | "order" : 0, 961 | "source" : [ "obj-58", 0 ] 962 | } 963 | 964 | } 965 | , { 966 | "patchline" : { 967 | "destination" : [ "obj-7", 0 ], 968 | "source" : [ "obj-6", 0 ] 969 | } 970 | 971 | } 972 | , { 973 | "patchline" : { 974 | "destination" : [ "obj-66", 0 ], 975 | "source" : [ "obj-65", 0 ] 976 | } 977 | 978 | } 979 | , { 980 | "patchline" : { 981 | "destination" : [ "obj-29", 0 ], 982 | "source" : [ "obj-66", 0 ] 983 | } 984 | 985 | } 986 | , { 987 | "patchline" : { 988 | "destination" : [ "obj-58", 0 ], 989 | "source" : [ "obj-68", 0 ] 990 | } 991 | 992 | } 993 | , { 994 | "patchline" : { 995 | "destination" : [ "obj-65", 0 ], 996 | "source" : [ "obj-68", 1 ] 997 | } 998 | 999 | } 1000 | , { 1001 | "patchline" : { 1002 | "destination" : [ "obj-53", 0 ], 1003 | "source" : [ "obj-69", 1 ] 1004 | } 1005 | 1006 | } 1007 | , { 1008 | "patchline" : { 1009 | "destination" : [ "obj-8", 0 ], 1010 | "source" : [ "obj-7", 0 ] 1011 | } 1012 | 1013 | } 1014 | , { 1015 | "patchline" : { 1016 | "destination" : [ "obj-65", 1 ], 1017 | "source" : [ "obj-72", 1 ] 1018 | } 1019 | 1020 | } 1021 | , { 1022 | "patchline" : { 1023 | "destination" : [ "obj-3", 0 ], 1024 | "source" : [ "obj-9", 0 ] 1025 | } 1026 | 1027 | } 1028 | ], 1029 | "dependency_cache" : [ { 1030 | "name" : "fluid.bufonsetslice~.mxo", 1031 | "type" : "iLaX" 1032 | } 1033 | , { 1034 | "name" : "fluid.bufnoveltyslice~.mxo", 1035 | "type" : "iLaX" 1036 | } 1037 | ], 1038 | "autosave" : 0, 1039 | "styles" : [ { 1040 | "name" : "max6box", 1041 | "default" : { 1042 | "accentcolor" : [ 0.8, 0.839216, 0.709804, 1.0 ], 1043 | "bgcolor" : [ 1.0, 1.0, 1.0, 0.5 ], 1044 | "textcolor_inverse" : [ 0.0, 0.0, 0.0, 1.0 ] 1045 | } 1046 | , 1047 | "parentstyle" : "", 1048 | "multi" : 0 1049 | } 1050 | , { 1051 | "name" : "max6inlet", 1052 | "default" : { 1053 | "color" : [ 0.423529, 0.372549, 0.27451, 1.0 ] 1054 | } 1055 | , 1056 | "parentstyle" : "", 1057 | "multi" : 0 1058 | } 1059 | , { 1060 | "name" : "max6message", 1061 | "default" : { 1062 | "bgfillcolor" : { 1063 | "type" : "gradient", 1064 | "color1" : [ 0.866667, 0.866667, 0.866667, 1.0 ], 1065 | "color2" : [ 0.788235, 0.788235, 0.788235, 1.0 ], 1066 | "color" : [ 0.290196, 0.309804, 0.301961, 1.0 ], 1067 | "angle" : 270.0, 1068 | "proportion" : 0.39, 1069 | "autogradient" : 0 1070 | } 1071 | , 1072 | "textcolor_inverse" : [ 0.0, 0.0, 0.0, 1.0 ] 1073 | } 1074 | , 1075 | "parentstyle" : "max6box", 1076 | "multi" : 0 1077 | } 1078 | , { 1079 | "name" : "max6outlet", 1080 | "default" : { 1081 | "color" : [ 0.0, 0.454902, 0.498039, 1.0 ] 1082 | } 1083 | , 1084 | "parentstyle" : "", 1085 | "multi" : 0 1086 | } 1087 | ] 1088 | } 1089 | 1090 | } 1091 | --------------------------------------------------------------------------------