├── resources ├── img │ ├── algo_1.png │ ├── algo_2.png │ ├── algo_3.png │ ├── algo_4.png │ ├── algo_5.png │ ├── algo_6.png │ ├── algo_7.png │ ├── algo_8.png │ ├── algo_9.png │ ├── logo.png │ ├── algo_10.png │ ├── algo_11.png │ ├── algo_12.png │ ├── algo_13.png │ ├── algo_14.png │ ├── algo_15.png │ ├── algo_16.png │ ├── algo_17.png │ ├── algo_18.png │ ├── algo_19.png │ ├── algo_20.png │ ├── algo_21.png │ ├── algo_22.png │ ├── algo_23.png │ ├── algo_24.png │ ├── algo_25.png │ ├── algo_26.png │ ├── algo_27.png │ ├── algo_28.png │ ├── algo_29.png │ ├── algo_30.png │ ├── algo_31.png │ ├── algo_32.png │ ├── bgcolor.png │ ├── algorithms.png │ └── render_algorithms.py ├── imgdoc │ ├── bt_bkg.png │ ├── pipeline.png │ └── bt_screenshot.png ├── pretrained │ ├── BASS 1.ts │ ├── BASS 2.ts │ ├── BASS-ROAD.ts │ ├── BRASS 1.ts │ ├── BRASS 2.ts │ ├── BRASS 3.ts │ ├── CALIOPE .ts │ ├── CLAV 1.ts │ ├── E.ORGAN 1.ts │ ├── E.PIANO 1.ts │ ├── FLUTE 1.ts │ ├── GUITAR 1.ts │ ├── GUITAR 2.ts │ ├── HARP 2.ts │ ├── HARPSICH .ts │ ├── KOTO .ts │ ├── LayerMelo.ts │ ├── Lunar.Bel.ts │ ├── MARIMBA .ts │ ├── OB8 BRASS.ts │ ├── ORCH-CHIM.ts │ ├── ORCHESTRA.ts │ ├── OSCAR BAS.ts │ ├── PIANO 1.ts │ ├── PIANO 2.ts │ ├── PIANO 3.ts │ ├── PIPES 1.ts │ ├── Pan Pipe .ts │ ├── REFS WHIS.ts │ ├── SHIMMER .ts │ ├── SITAR .ts │ ├── STEEL DRU.ts │ ├── STRINGS 1.ts │ ├── STRINGS 2.ts │ ├── STRINGS 3.ts │ ├── SYN CLAVC.ts │ ├── SYN-LEAD .ts │ ├── Smoothout.ts │ ├── TBTAN BEL.ts │ ├── TIMPANI .ts │ ├── TUB BELLS.ts │ ├── TWINCLE .ts │ ├── VIBE 1.ts │ ├── VOICE 1.ts │ ├── WineGlass.ts │ └── tingHarmi.ts ├── magic2.xml ├── v4.xml ├── magic.xml └── magic_bake.xml ├── .gitignore ├── src ├── FeatureProcessing │ ├── Yin.hpp │ ├── RMSProcessor.hpp │ ├── PitchTrackManager.hpp │ ├── FeatureRegister.hpp │ ├── RMSProcessor.cpp │ └── Yin.cpp ├── ParameterIDs.hpp ├── Inference │ ├── TorchInference.hpp │ ├── EnvModels.hpp │ ├── EnvModels.cpp │ └── TorchInference.cpp ├── GuiItems │ ├── RatiosBar.cpp │ ├── StatusBar.cpp │ ├── DrawableLabel.cpp │ ├── ModelComboBox.cpp │ └── GuiItems.hpp ├── PluginProcessorModelRoutines.cpp ├── FMSynth │ ├── FMSynth.hpp │ ├── FMSynth.cpp │ └── algorithms.hpp ├── PluginConfig.hpp ├── PluginProcessor.hpp └── PluginProcessor.cpp ├── cmake └── TorchUtils.cmake ├── README.md ├── .github └── workflows │ └── build.yaml └── CMakeLists.txt /resources/img/algo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_1.png -------------------------------------------------------------------------------- /resources/img/algo_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_2.png -------------------------------------------------------------------------------- /resources/img/algo_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_3.png -------------------------------------------------------------------------------- /resources/img/algo_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_4.png -------------------------------------------------------------------------------- /resources/img/algo_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_5.png -------------------------------------------------------------------------------- /resources/img/algo_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_6.png -------------------------------------------------------------------------------- /resources/img/algo_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_7.png -------------------------------------------------------------------------------- /resources/img/algo_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_8.png -------------------------------------------------------------------------------- /resources/img/algo_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_9.png -------------------------------------------------------------------------------- /resources/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/logo.png -------------------------------------------------------------------------------- /resources/img/algo_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_10.png -------------------------------------------------------------------------------- /resources/img/algo_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_11.png -------------------------------------------------------------------------------- /resources/img/algo_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_12.png -------------------------------------------------------------------------------- /resources/img/algo_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_13.png -------------------------------------------------------------------------------- /resources/img/algo_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_14.png -------------------------------------------------------------------------------- /resources/img/algo_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_15.png -------------------------------------------------------------------------------- /resources/img/algo_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_16.png -------------------------------------------------------------------------------- /resources/img/algo_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_17.png -------------------------------------------------------------------------------- /resources/img/algo_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_18.png -------------------------------------------------------------------------------- /resources/img/algo_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_19.png -------------------------------------------------------------------------------- /resources/img/algo_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_20.png -------------------------------------------------------------------------------- /resources/img/algo_21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_21.png -------------------------------------------------------------------------------- /resources/img/algo_22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_22.png -------------------------------------------------------------------------------- /resources/img/algo_23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_23.png -------------------------------------------------------------------------------- /resources/img/algo_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_24.png -------------------------------------------------------------------------------- /resources/img/algo_25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_25.png -------------------------------------------------------------------------------- /resources/img/algo_26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_26.png -------------------------------------------------------------------------------- /resources/img/algo_27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_27.png -------------------------------------------------------------------------------- /resources/img/algo_28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_28.png -------------------------------------------------------------------------------- /resources/img/algo_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_29.png -------------------------------------------------------------------------------- /resources/img/algo_30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_30.png -------------------------------------------------------------------------------- /resources/img/algo_31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_31.png -------------------------------------------------------------------------------- /resources/img/algo_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algo_32.png -------------------------------------------------------------------------------- /resources/img/bgcolor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/bgcolor.png -------------------------------------------------------------------------------- /resources/img/algorithms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/img/algorithms.png -------------------------------------------------------------------------------- /resources/imgdoc/bt_bkg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/imgdoc/bt_bkg.png -------------------------------------------------------------------------------- /resources/imgdoc/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/imgdoc/pipeline.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | testmodels/ 2 | build/ 3 | .vscode 4 | .DS_Store 5 | defaults.cfg 6 | src/print_alg.py 7 | libtorch 8 | *~ -------------------------------------------------------------------------------- /resources/imgdoc/bt_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/imgdoc/bt_screenshot.png -------------------------------------------------------------------------------- /resources/pretrained/BASS 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/BASS 1.ts -------------------------------------------------------------------------------- /resources/pretrained/BASS 2.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/BASS 2.ts -------------------------------------------------------------------------------- /resources/pretrained/BASS-ROAD.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/BASS-ROAD.ts -------------------------------------------------------------------------------- /resources/pretrained/BRASS 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/BRASS 1.ts -------------------------------------------------------------------------------- /resources/pretrained/BRASS 2.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/BRASS 2.ts -------------------------------------------------------------------------------- /resources/pretrained/BRASS 3.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/BRASS 3.ts -------------------------------------------------------------------------------- /resources/pretrained/CALIOPE .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/CALIOPE .ts -------------------------------------------------------------------------------- /resources/pretrained/CLAV 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/CLAV 1.ts -------------------------------------------------------------------------------- /resources/pretrained/E.ORGAN 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/E.ORGAN 1.ts -------------------------------------------------------------------------------- /resources/pretrained/E.PIANO 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/E.PIANO 1.ts -------------------------------------------------------------------------------- /resources/pretrained/FLUTE 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/FLUTE 1.ts -------------------------------------------------------------------------------- /resources/pretrained/GUITAR 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/GUITAR 1.ts -------------------------------------------------------------------------------- /resources/pretrained/GUITAR 2.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/GUITAR 2.ts -------------------------------------------------------------------------------- /resources/pretrained/HARP 2.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/HARP 2.ts -------------------------------------------------------------------------------- /resources/pretrained/HARPSICH .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/HARPSICH .ts -------------------------------------------------------------------------------- /resources/pretrained/KOTO .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/KOTO .ts -------------------------------------------------------------------------------- /resources/pretrained/LayerMelo.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/LayerMelo.ts -------------------------------------------------------------------------------- /resources/pretrained/Lunar.Bel.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/Lunar.Bel.ts -------------------------------------------------------------------------------- /resources/pretrained/MARIMBA .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/MARIMBA .ts -------------------------------------------------------------------------------- /resources/pretrained/OB8 BRASS.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/OB8 BRASS.ts -------------------------------------------------------------------------------- /resources/pretrained/ORCH-CHIM.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/ORCH-CHIM.ts -------------------------------------------------------------------------------- /resources/pretrained/ORCHESTRA.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/ORCHESTRA.ts -------------------------------------------------------------------------------- /resources/pretrained/OSCAR BAS.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/OSCAR BAS.ts -------------------------------------------------------------------------------- /resources/pretrained/PIANO 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/PIANO 1.ts -------------------------------------------------------------------------------- /resources/pretrained/PIANO 2.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/PIANO 2.ts -------------------------------------------------------------------------------- /resources/pretrained/PIANO 3.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/PIANO 3.ts -------------------------------------------------------------------------------- /resources/pretrained/PIPES 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/PIPES 1.ts -------------------------------------------------------------------------------- /resources/pretrained/Pan Pipe .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/Pan Pipe .ts -------------------------------------------------------------------------------- /resources/pretrained/REFS WHIS.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/REFS WHIS.ts -------------------------------------------------------------------------------- /resources/pretrained/SHIMMER .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/SHIMMER .ts -------------------------------------------------------------------------------- /resources/pretrained/SITAR .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/SITAR .ts -------------------------------------------------------------------------------- /resources/pretrained/STEEL DRU.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/STEEL DRU.ts -------------------------------------------------------------------------------- /resources/pretrained/STRINGS 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/STRINGS 1.ts -------------------------------------------------------------------------------- /resources/pretrained/STRINGS 2.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/STRINGS 2.ts -------------------------------------------------------------------------------- /resources/pretrained/STRINGS 3.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/STRINGS 3.ts -------------------------------------------------------------------------------- /resources/pretrained/SYN CLAVC.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/SYN CLAVC.ts -------------------------------------------------------------------------------- /resources/pretrained/SYN-LEAD .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/SYN-LEAD .ts -------------------------------------------------------------------------------- /resources/pretrained/Smoothout.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/Smoothout.ts -------------------------------------------------------------------------------- /resources/pretrained/TBTAN BEL.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/TBTAN BEL.ts -------------------------------------------------------------------------------- /resources/pretrained/TIMPANI .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/TIMPANI .ts -------------------------------------------------------------------------------- /resources/pretrained/TUB BELLS.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/TUB BELLS.ts -------------------------------------------------------------------------------- /resources/pretrained/TWINCLE .ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/TWINCLE .ts -------------------------------------------------------------------------------- /resources/pretrained/VIBE 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/VIBE 1.ts -------------------------------------------------------------------------------- /resources/pretrained/VOICE 1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/VOICE 1.ts -------------------------------------------------------------------------------- /resources/pretrained/WineGlass.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/WineGlass.ts -------------------------------------------------------------------------------- /resources/pretrained/tingHarmi.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcaspe/BesselsTrick/HEAD/resources/pretrained/tingHarmi.ts -------------------------------------------------------------------------------- /src/FeatureProcessing/Yin.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | File: YIN.hpp 3 | 4 | YIN F0 Estimator, adapted by Franco Caspe for block-based operation. 5 | Adapted from: https://github.com/JorenSix/Pidato 6 | 7 | Original project license: GNU LESSER GENERAL PUBLIC LICENSE 8 | Version 3, 29 June 2007 9 | 10 | */ 11 | #ifndef Yin_h 12 | #define Yin_h 13 | 14 | class Yin { 15 | public: 16 | Yin(); 17 | ~Yin(); 18 | void init(float sampleRate, int bufferSize, int frameSize, 19 | float yinThreshold, bool downsample_x2); 20 | void updateBuffer(const float* frame); 21 | float getPitch(); 22 | float getProbability(); 23 | void setThreshold(float threshold); 24 | 25 | private: 26 | float parabolicInterpolation(int tauEstimate); 27 | 28 | private: 29 | int absoluteThreshold(); 30 | 31 | private: 32 | void cumulativeMeanNormalizedDifference(); 33 | 34 | private: 35 | void difference(); 36 | 37 | private: 38 | float _threshold; 39 | int _bufferSize; 40 | int _halfBufferSize; 41 | int _frameSize; 42 | int _writeIdx; 43 | float* _audioBuffer; 44 | float _sampleRate; 45 | float* _yinBuffer; 46 | float _probability; 47 | float _input_gain; 48 | int _fillCounter; 49 | bool _downsample_x2; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /resources/img/render_algorithms.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import numpy as np 3 | 4 | def set_background(img,r,g,b): 5 | print(f"set bkf img {img.shape}") 6 | mask = (image[:,:,3] == 0) 7 | image[mask,0] = r 8 | image[mask,1] = g 9 | image[mask,2] = b 10 | return image 11 | 12 | def change_color(img,ir,ig,ib,dr,dg,db): 13 | print(f"change color {img.shape}") 14 | mask = ((image[:,:,0] == ir) * (image[:,:,1] == ig) * (image[:,:,2] == ib)) 15 | image[mask,0] = dr 16 | image[mask,1] = dg 17 | image[mask,2] = db 18 | return image 19 | 20 | 21 | image = Image.open('algorithms.png') 22 | 23 | print(image.size) 24 | image = np.asarray(image).copy() 25 | 26 | 27 | jump_y = (image.shape[0]-2)//8 28 | jump_x = image.shape[1]//4-2 29 | 30 | # Background color 12083D = 18,8,61 31 | #image = set_background(image,18,8,61) 32 | image = change_color(image,255,255,255,255,0,0) 33 | image = change_color(image,0,0,0,255,255,255) 34 | image = change_color(image,255,0,0,127,127,127) 35 | #image[image[:,:,3] == 0] = 255 36 | print(image.shape) 37 | chunk = image[0:jump_y,0:jump_x,0:3] 38 | 39 | ## Chunk template but keep original transparency as is 40 | for y in range (8): 41 | for x in range(4): 42 | chunk = image[y*jump_y:jump_y*(y+1), 43 | x*jump_x:jump_x*(x+1),0:4] 44 | 45 | im = Image.fromarray(chunk) 46 | im.save(f"algo_{1+x+y*4}.png") -------------------------------------------------------------------------------- /cmake/TorchUtils.cmake: -------------------------------------------------------------------------------- 1 | 2 | function(get_torch_libs libs) 3 | 4 | if (NOT TORCH_INSTALL_PREFIX) 5 | message(FATAL_ERROR "TORCH_INSTALL_PREFIX is not set") 6 | endif() 7 | 8 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 9 | 10 | set( 11 | TORCH_LIBS 12 | ${TORCH_INSTALL_PREFIX}/lib/libtorch.dylib 13 | ${TORCH_INSTALL_PREFIX}/lib/libtorch_cpu.dylib 14 | ${TORCH_INSTALL_PREFIX}/lib/libc10.dylib 15 | ) 16 | 17 | # if the architecture is intel, we need to add the libiomp5 library 18 | if (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64") 19 | list(APPEND TORCH_LIBS ${TORCH_INSTALL_PREFIX}/lib/libiomp5.dylib) 20 | endif() 21 | 22 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") 23 | 24 | set( 25 | TORCH_LIBS 26 | ${TORCH_INSTALL_PREFIX}/lib/libtorch.so 27 | ${TORCH_INSTALL_PREFIX}/lib/libtorch_cpu.so 28 | ${TORCH_INSTALL_PREFIX}/lib/libc10.so 29 | ${TORCH_INSTALL_PREFIX}/lib/libgomp-52f2fd74.so.1 30 | ) 31 | else() 32 | message(FATAL_ERROR "Unsupported system: ${CMAKE_SYSTEM_NAME}") 33 | endif() 34 | 35 | set(${libs} ${TORCH_LIBS} PARENT_SCOPE) 36 | 37 | endfunction() 38 | 39 | # copy the torch libraries to the build directory 40 | function(copy_torch_libs target) 41 | 42 | get_torch_libs(TORCH_LIBS) 43 | add_custom_command( 44 | TARGET ${target} 45 | POST_BUILD 46 | COMMAND ${CMAKE_COMMAND} 47 | ARGS -E copy ${TORCH_LIBS} "$" 48 | COMMENT "Copy Torch Libraries to ${target}" 49 | ) 50 | 51 | endfunction() -------------------------------------------------------------------------------- /src/FeatureProcessing/RMSProcessor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: RMSProcessor.hpp 36 | Computes block-wise input audio RMS over a window. 37 | */ 38 | 39 | class RMS_Processor { 40 | public: 41 | RMS_Processor() { 42 | _block_size = 256; 43 | _measure_count = 0; 44 | _n_entries = 0; 45 | } 46 | ~RMS_Processor(); 47 | void init(int windowSize, int blockSize, bool linear_output); 48 | float process(const float* frame); 49 | 50 | private: 51 | float* _rms_buffer = 52 | 0; // Buffer to store past measures size = (windowSize/blockSize) 53 | int _measure_count; // Current Circular Buffer position. 54 | int _block_size; // Block size (in samples). 55 | int _n_entries; // Window size (in blocks). 56 | bool _use_linear_output = false; 57 | }; -------------------------------------------------------------------------------- /src/ParameterIDs.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SRC_PARAMERERIDS_HPP_ 3 | #define SRC_PARAMERERIDS_HPP_ 4 | #endif // SRC_ALGORITHMS_HPP_ 5 | 6 | namespace ValTree_IDs { 7 | static juce::Identifier gui_params ("gui_params"); // pre-create an Identifier 8 | } 9 | 10 | // ID's for objects in GUI 11 | namespace GUI_IDs { 12 | static juce::String status{"lbl_status"}; 13 | static juce::String models{"combobox_models"}; 14 | static juce::String algorithm{"knob_algo"}; 15 | static juce::String algoplot{"label_algo"}; 16 | static juce::String inGain{"knob_input_gain"}; 17 | static juce::String outGain{"knob_output_gain"}; 18 | 19 | static juce::String fmBoost1{"knob_boost1"}; 20 | static juce::String fmBoost2{"knob_boost2"}; 21 | static juce::String fmBoost3{"knob_boost3"}; 22 | static juce::String fmBoost4{"knob_boost4"}; 23 | static juce::String fmBoost5{"knob_boost5"}; 24 | static juce::String fmBoost6{"knob_boost6"}; 25 | 26 | static juce::String fmFine1{"knob_fine1"}; 27 | static juce::String fmFine2{"knob_fine2"}; 28 | static juce::String fmFine3{"knob_fine3"}; 29 | static juce::String fmFine4{"knob_fine4"}; 30 | static juce::String fmFine5{"knob_fine5"}; 31 | static juce::String fmFine6{"knob_fine6"}; 32 | 33 | static juce::String fmCoarse1{"knob_coarse1"}; 34 | static juce::String fmCoarse2{"knob_coarse2"}; 35 | static juce::String fmCoarse3{"knob_coarse3"}; 36 | static juce::String fmCoarse4{"knob_coarse4"}; 37 | static juce::String fmCoarse5{"knob_coarse5"}; 38 | static juce::String fmCoarse6{"knob_coarse6"}; 39 | 40 | } // namespace GUI_IDs 41 | 42 | // IDs for properties in ValueTree 43 | namespace IDs { 44 | static juce::String cboxselectedid{"cboxselectedid"}; 45 | static juce::String inGain{"in_gain"}; 46 | static juce::String outGain{"out_gain"}; 47 | static juce::String algorithm{"algorithm"}; 48 | 49 | static juce::String fmBoost1{"boost1"}; 50 | static juce::String fmBoost2{"boost2"}; 51 | static juce::String fmBoost3{"boost3"}; 52 | static juce::String fmBoost4{"boost4"}; 53 | static juce::String fmBoost5{"boost5"}; 54 | static juce::String fmBoost6{"boost6"}; 55 | 56 | static juce::String fmFine1{"fine1"}; 57 | static juce::String fmFine2{"fine2"}; 58 | static juce::String fmFine3{"fine3"}; 59 | static juce::String fmFine4{"fine4"}; 60 | static juce::String fmFine5{"fine5"}; 61 | static juce::String fmFine6{"fine6"}; 62 | 63 | static juce::String fmCoarse1{"coarse1"}; 64 | static juce::String fmCoarse2{"coarse2"}; 65 | static juce::String fmCoarse3{"coarse3"}; 66 | static juce::String fmCoarse4{"coarse4"}; 67 | static juce::String fmCoarse5{"coarse5"}; 68 | static juce::String fmCoarse6{"coarse6"}; 69 | 70 | static juce::String debug1{"debug1"}; 71 | static juce::String debug2{"debug2"}; 72 | static juce::String debug3{"debug3"}; 73 | static juce::String debug5{"debug5"}; 74 | static juce::String debug6{"debug6"}; 75 | static juce::String debug7{"debug7"}; 76 | static juce::String debug8{"debug8"}; 77 | static juce::String debug9{"debug9"}; 78 | 79 | static juce::Identifier oscilloscope{"oscilloscope"}; 80 | } // namespace IDs -------------------------------------------------------------------------------- /src/Inference/TorchInference.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: TorchInference.hpp 36 | Wrapper for RNN inference using libtorch. 37 | */ 38 | 39 | #include // One-stop header. 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | struct InferenceConfig { 49 | std::string filename; 50 | std::array input_sizes; // Size of input tensor 51 | std::array fixed_slices; 52 | int rolling_slice = 53 | 2; // Slice ID to iterate through and extract output values 54 | int output_len = 128; // Total length of output 55 | int state_len = 0; 56 | }; 57 | 58 | class TorchModel { 59 | public: 60 | TorchModel(); 61 | void init(InferenceConfig config); 62 | void get_patch(std::array &dest); 63 | bool contains_patch(); 64 | void call(std::array input_array, std::vector &output_array); 65 | void call(std::array input_array, std::vector &output_array, 66 | std::vector &state_array); 67 | 68 | private: 69 | torch::jit::script::Module _module; 70 | InferenceConfig _config = InferenceConfig(); 71 | bool _isTypeES = false; 72 | }; -------------------------------------------------------------------------------- /src/GuiItems/RatiosBar.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: RatiosBar.cpp 36 | Implements RatiosBar GUI object. 37 | */ 38 | #include "GuiItems.hpp" 39 | 40 | 41 | RatiosBarItem::RatiosBarItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node) : foleys::GuiItem (builder, node) 42 | { 43 | addAndMakeVisible (label); 44 | startTimerHz(30); 45 | } 46 | 47 | void RatiosBarItem::refresh() 48 | { 49 | int id = getProperty("osc_id"); 50 | if(id>=1 && id<=6) 51 | { 52 | auto *guiconfig = magicBuilder.getMagicState().getObjectWithType("guiconfig"); 53 | if(guiconfig) 54 | { 55 | auto coarse = guiconfig->fm_coarse[id-1]; 56 | auto fine = guiconfig->fm_fine[id-1]; 57 | 58 | float f = (coarse == 0) ? 0.5f : (float)coarse; 59 | f = f + (f / 100) * ((float)fine); 60 | label.setText(juce::String("f = ") + juce::String(f),juce::dontSendNotification); 61 | } 62 | } 63 | } 64 | 65 | std::vector RatiosBarItem::getSettableProperties() const 66 | { 67 | std::vector newProperties; 68 | 69 | newProperties.push_back ({ configNode, "osc_id", foleys::SettableProperty::Number, 1, {} }); 70 | 71 | return newProperties; 72 | } 73 | 74 | void RatiosBarItem::timerCallback() 75 | { 76 | refresh(); 77 | } 78 | 79 | // Sets label callback and updates model list. 80 | void RatiosBarItem::update() 81 | { 82 | refresh(); 83 | } -------------------------------------------------------------------------------- /src/GuiItems/StatusBar.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: StatusBar.cpp 36 | Implements StatusBar GUI object. 37 | */ 38 | 39 | #include "GuiItems.hpp" 40 | 41 | StatusBarItem::StatusBarItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node) : foleys::GuiItem (builder, node) 42 | { 43 | addAndMakeVisible (label); 44 | } 45 | 46 | void StatusBarItem::update_status_message() 47 | { 48 | if (auto* processor = dynamic_cast(magicBuilder.getMagicState().getProcessor())) 49 | { 50 | auto *guiconfig = magicBuilder.getMagicState().getObjectWithType("guiconfig"); 51 | if(guiconfig) 52 | { 53 | // Update Status message 54 | if(guiconfig->modelnames.size()== 0) 55 | { 56 | guiconfig->status = "Empty model list. Open pretrained directory."; 57 | } 58 | else 59 | { 60 | // Check if a model is loaded 61 | if(processor->_model.get() == nullptr) 62 | guiconfig->status = "Select a model from list."; 63 | else 64 | guiconfig->status = "Ready to play!"; 65 | } 66 | } 67 | } 68 | return; 69 | } 70 | 71 | // Sets label callback and updates model list. 72 | void StatusBarItem::update() 73 | { 74 | update_status_message(); 75 | auto *guiconfig = magicBuilder.getMagicState().getObjectWithType("guiconfig"); 76 | if(!guiconfig) return; 77 | label.setText(guiconfig->status,juce::dontSendNotification); 78 | } -------------------------------------------------------------------------------- /src/Inference/EnvModels.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: EnvModels.hpp: 36 | Introuduce wrapper classes to hide Libtorch implementation of 37 | the Envelope RNNs. 38 | */ 39 | 40 | #include "TorchInference.hpp" 41 | 42 | class EnvModel { 43 | public: 44 | virtual void init(const std::string &filename, 45 | std::array model_input_sizes, int n_outputs) = 0; 46 | virtual void init(const std::string &filename, 47 | std::array model_input_sizes, int n_outputs, 48 | int n_state) = 0; 49 | virtual std::vector get_state() = 0; 50 | virtual std::vector call(float pitch, float loudness) = 0; 51 | virtual bool reset_state() = 0; 52 | virtual ~EnvModel(); 53 | bool is_standalone() { return _isStandalone; } 54 | bool contains_patch() { return _containsPatch; } 55 | std::array get_patch() { return _initial_patch; } 56 | 57 | protected: 58 | EnvModel(); 59 | std::unique_ptr _torchmodel; 60 | // We have to use vector cause we dont know the size yet until init is called 61 | std::vector _outbuffer; 62 | std::vector _statebuffer; 63 | bool _isStandalone = false; 64 | bool _containsPatch = false; 65 | float normalize_pitch(float pitch); 66 | float normalize_loudness(float loudness); 67 | std::array _initial_patch = {0}; 68 | }; 69 | 70 | class GRUModel : public EnvModel { 71 | public: 72 | std::vector call(float pitch, float loudness) override; 73 | std::vector get_state() override; 74 | bool reset_state() override; 75 | void init(const std::string &filename, std::array model_input_sizes, 76 | int n_outputs) override; 77 | void init(const std::string &filename, std::array model_input_sizes, 78 | int n_outputs, int n_state) override; 79 | }; 80 | -------------------------------------------------------------------------------- /src/FeatureProcessing/PitchTrackManager.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: PitchTrackManager.hpp 36 | Pitch tracking manager for multiscale pitch detection using multiple trackers. 37 | */ 38 | 39 | template class PitchTrackManager { 40 | public: 41 | PitchTrackManager() {} 42 | 43 | // Feed a bufferSizes array, from smaller to bigger 44 | void init(float sampleRate, std::array bufferSizes, int frameSize, 45 | float yinThreshold, std::array downsample_x2) 46 | { 47 | for(int i = 0; iinit(sampleRate,bufferSizes[i],frameSize,yinThreshold,downsample_x2[i]); 50 | _limit_freqs[i] = 2*((10*sampleRate)/(bufferSizes[i]*9)); 51 | std::cout << "[TRACK MGR] Buffer size is " << bufferSizes[i] \ 52 | << " lim freq is " << _limit_freqs[i] << std::endl; 53 | } 54 | return; 55 | } 56 | 57 | void reset() 58 | { 59 | for(int i = 0; iupdateBuffer(frame); 72 | } 73 | 74 | // Assumes classes are aranged from smaller to bigger 75 | // Computes the from the faster tracker, and if not available goes to the slower ones. 76 | float getPitch() 77 | { 78 | float pitch; 79 | for(int i = 0; igetPitch(); 82 | if(pitch != -1 && pitch !=0 & pitch > _limit_freqs[i]) 83 | break; 84 | } 85 | return pitch; 86 | } 87 | 88 | void setThreshold(float threshold) 89 | { 90 | for(int i = 0; isetThreshold(threshold); 92 | } 93 | 94 | private: 95 | std::unique_ptr _trackers[num_units]; 96 | float _limit_freqs[num_units]; 97 | }; 98 | -------------------------------------------------------------------------------- /src/GuiItems/DrawableLabel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: DrawableLabel.cpp 36 | Implements DrawableLabel GUI object. 37 | */ 38 | 39 | #include "GuiItems.hpp" 40 | #include "BinaryData.h" 41 | 42 | DrawableLabel::DrawableLabel() 43 | { 44 | // make sure you define some default colour, otherwise the juce lookup will choke 45 | setColour (backgroundColourId, juce::Colours::black); 46 | setColour (drawColourId, juce::Colours::green); 47 | setColour (fillColourId, juce::Colours::green.withAlpha (0.5f)); 48 | } 49 | 50 | void DrawableLabel::setAlgorithm(int number) 51 | { 52 | if(number > 0 && number < 33) 53 | _algorithm = number; 54 | } 55 | 56 | void DrawableLabel::paint (juce::Graphics& g) 57 | { 58 | juce::Image myImage; 59 | 60 | myImage = juce::ImageFileFormat::loadFrom( 61 | _algoplots[_algorithm-1], 62 | _algoplotSizes[_algorithm-1]); 63 | 64 | g.drawImage(myImage,0,0,getWidth(),getHeight(), 65 | 0,0,myImage.getWidth(),myImage.getHeight()); 66 | } 67 | 68 | 69 | DrawableLabelItem::DrawableLabelItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node): foleys::GuiItem (builder, node) 70 | { 71 | // Create the colour names to have them configurable 72 | setColourTranslation ({ 73 | {"drawablelabel-background", DrawableLabel::backgroundColourId}, 74 | {"drawablelabel-draw", DrawableLabel::drawColourId}, 75 | {"drawablelabel-fill", DrawableLabel::fillColourId} }); 76 | 77 | addAndMakeVisible (drawablelabel); 78 | startTimerHz (30); 79 | } 80 | 81 | // Override update() to set the GUI values to your custom component 82 | void DrawableLabelItem::update() 83 | { 84 | 85 | auto *guiconfig = magicBuilder.getMagicState().getObjectWithType("guiconfig"); 86 | if(guiconfig) 87 | { 88 | //std::cout << "[UPDATE] alg: " << int(*alg)<< std::endl; 89 | drawablelabel.setAlgorithm (guiconfig->fm_algo); 90 | } 91 | drawablelabel.repaint(); 92 | } 93 | 94 | void DrawableLabelItem::timerCallback() 95 | { 96 | update(); 97 | } 98 | 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | Logo 4 |

5 | 6 | **Bessel's Trick** is a `Neural Audio Plugin` for **fast, live** Tone Transformation of Musical Instrument sounds using [Frequency Modulation](https://en.wikipedia.org/wiki/Frequency_modulation_synthesis) (FM) synthesis. 7 | 8 | [![GitHub release](https://img.shields.io/github/release/fcaspe/BesselsTrick.svg)](https://GitHub.com/fcaspe/BesselsTrick/releases/) 9 | 10 | 11 | ### Features: 12 | - FM Synth controllable from an Audio Input. 13 | - Try feedng it voice, violin, solo guitar, trumpet. 14 | - Designed for live playing. 15 | - Fast sound generation, *1.3ms inference latency* 16 | - 6-oscillator FM engine. 17 | - Based on the classic Yamaha DX7. 18 | - Comes with many pretrained sounds! 19 | - See the Release webpage. 20 | - Tweakable FM controls on real time. 21 | - VST3 and AU plugin formats available. 22 | 23 |   24 | 25 |

26 | 27 |

28 | 29 |

30 | Plugin's GUI 31 |

32 | 33 | ## Build it 34 | 35 | Clone this repository and run: 36 | 37 | ```bash 38 | source build.sh 39 | ``` 40 | 41 | This will run the build and install process. 42 | 43 | ## Bringing in more sounds 44 | 45 | Bringing more sounds require obtaining an FM patch in DX7 format and then training a new model. For info on how to train new models visit the [Envelope Learning](https://github.com/fcaspe/fmtransfer) repository. 46 | 47 | ## How does it work? 48 | 49 |

50 | Logo 51 |

52 | 53 | Bessel's Trick does not use MIDI to control the synth. Instead, it works in an `audio to audio` fashion, similar to other [Tone Transfer](https://sites.research.google/tonetransfer) approaches. 54 | 55 | The plugin extracts continuous sequences of pitch and RMS from a `monophonic` audio signal, and uses small neural nets (one for each sound) to map these to the envelopes of a six-oscillator FM synth. 56 | 57 | But, unlike previous approaches, I use a different method for training the neural nets, that addresses the poor articulation present in previous methods. If you want to learn more check out my [Envelope Learning](https://fcaspe.github.io/fmtransfer) paper. 58 | 59 | ### Why FM? 60 | 61 | Frequency Modulation synthesis is a versatile and compact approach for synthesizing a wide range of timbres. Moreover, there is a wide variety of sounds already designed, particularly in the form of **Yamaha DX7** Patches. 62 | 63 | [Envelope Learning](https://fcaspe.github.io/fmtransfer) learns to control envelopes of synthesizer models. We use **Yamaha DX7** patches to train the neural nets. 64 | 65 | ### Why Neural Nets? 66 | 67 | Each sound is implemented with a really tiny Recurrent Neural Network (RNN) that has been trained to replicate the oscillators' amplitude envelopes from a `continuous control` signals. Players have continuous influence on the sound throughout the playing action. 68 | 69 | This continuous control enables some cool articulations for free! For instance, on a guitar, Bessel's Trick can support String Muting, Hammer-on, Pull-off, and Natural harmonics. 70 | 71 | Try it with other *monophonic* instruments! 72 | 73 | ## Acknowledgements 74 | 75 | I would like to thank these lovely folks for their time! 76 | - Jeff Miller (GUI design tips) 77 | - Rodrigo Diaz (Building Pipeline, Apple Notarization) 78 | - Daniel Walz (PGM library tips) 79 | 80 | ## Licenses 81 | - Bessel's Trick released under BSD-3. 82 | - Foleys GUI Magic Library licensed under BSD-3. 83 | - YIN Pitch Tracker adapted from [Pidato](https://github.com/JorenSix/Pidato), licensed under GNU GPL 3. 84 | 85 | ## Citation 86 | If you find this work useful, please consider citing us: 87 | 88 | ```bibtex 89 | @article{caspe2023envelopelearning, 90 | title={{FM Tone Transfer with Envelope Learning}}, 91 | author={Caspe, Franco and McPherson, Andrew and Sandler, Mark}, 92 | journal={Proceedings of Audio Mostly 2023}, 93 | year={2023} 94 | } 95 | ``` -------------------------------------------------------------------------------- /src/FeatureProcessing/FeatureRegister.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: FeatureRegister.hpp: 36 | 37 | Feature register to sync RMS excitation with valid F0 38 | It features two working modes: 39 | SYNC: Generates valid RMS only when f0 is present 40 | LATCH: GENERATES valid RMS when f0 is present, 41 | registering that value until the end of the f0. 42 | 43 | If disabled, this module just returns the original rms. 44 | */ 45 | class FeatureRegister { 46 | public: 47 | enum State { 48 | DISABLED, 49 | WAITING, 50 | TRIGGERED, 51 | }; 52 | enum WorkingMode { SYNC, LATCH }; 53 | FeatureRegister() { 54 | _state = WAITING; 55 | _mode = SYNC; 56 | _registered_rms = 0.0f; 57 | } 58 | void setState(bool enable) { 59 | std::cout << "[FEATREG] State set: " << \ 60 | ((enable)? "WAITING" : "DISABLED" )\ 61 | << std::endl; 62 | _state = enable ? WAITING : DISABLED; 63 | } 64 | void setMode(WorkingMode mode) { 65 | std::cout << "[FEATREG] Mode set: " << \ 66 | ((mode == SYNC)? "SYNC" : "LATCH")\ 67 | << std::endl; 68 | _mode = mode; 69 | } 70 | float run(float f0, float rms) { 71 | float rms_return = 0.0f; 72 | switch (_state) { 73 | case DISABLED: 74 | rms_return = rms; 75 | break; 76 | case WAITING: 77 | if (f0 > 0.0f) // Trigger condition 78 | { 79 | rms_return = rms; 80 | _registered_rms = rms; 81 | _state = TRIGGERED; 82 | } else // No trigger 83 | { 84 | rms_return = 0.0f; 85 | _state = WAITING; 86 | } 87 | break; 88 | case TRIGGERED: 89 | if (f0 <= 0.0f) { 90 | rms_return = 0.0f; 91 | _state = WAITING; 92 | } else { 93 | if (_mode == SYNC) 94 | rms_return = rms; 95 | else if (_mode == LATCH) 96 | rms_return = _registered_rms; 97 | } 98 | break; 99 | default: 100 | break; 101 | } 102 | return rms_return; 103 | } 104 | 105 | private: 106 | State _state; 107 | WorkingMode _mode; 108 | float _registered_rms; 109 | }; 110 | -------------------------------------------------------------------------------- /src/PluginProcessorModelRoutines.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: PluginProcessorModelRoutines.cpp 36 | Plugin routines for switching models and modifying FM patches 37 | */ 38 | 39 | #include "PluginProcessor.hpp" 40 | 41 | void BesselsProcessor::apply_config() { 42 | _fmsynth->set_config(_config.fm_config); 43 | _fmsynth->set_ratios(_config.fm_coarse,_config.fm_fine); 44 | //_pitch_tracker->setThreshold(_config.yin_threshold); 45 | _tracker_manager.setThreshold(_config.yin_threshold); 46 | } 47 | 48 | void BesselsProcessor::reload_model(const unsigned int entry) { 49 | auto *guiconfig = magicState.getObjectWithType("guiconfig"); 50 | if(!guiconfig) return; 51 | 52 | // Ensure entry is within range 53 | if(entry > guiconfig->modelnames.size()) return; 54 | std::cout << "BesselsProcessor::reload_model() - Loading " 55 | << (guiconfig->modelnames)[entry] 56 | << " - state: " << guiconfig->nstates[entry] << std::endl; 57 | 58 | load_gru_model((guiconfig->modeldir) + "/" + guiconfig->modelfilenames[entry], guiconfig->nstates[entry]); 59 | 60 | std::cout << "\tContains patch:" << _model->contains_patch() << std::endl; 61 | if (_model->contains_patch()) { 62 | _fmsynth->load_dx7_config(_model->get_patch()); 63 | _config.fm_config = _fmsynth->get_config(); 64 | _config.fm_coarse = _fmsynth->get_fr_coarse(); 65 | _config.fm_fine = _fmsynth->get_fr_fine(); 66 | } 67 | } 68 | 69 | // Load a standalone gru model 70 | void BesselsProcessor::load_gru_model(const std::string& model_path) { 71 | load_gru_model(model_path, 72 | 0); // Hidden size not specified for standalone model. 73 | } 74 | 75 | void BesselsProcessor::load_gru_model(const std::string& model_path, 76 | int n_state) { 77 | std::cout << " load_gru_model() " << model_path << " " 78 | << n_state << std::endl; 79 | bool old_skip_switch_val = _config.skipInference; 80 | _config.skipInference = true; // Override config value 81 | _model.reset(new GRUModel()); 82 | 83 | std::array model_input_sizes = {1, 1, 2}; 84 | constexpr int n_outputs = 6; 85 | // Init for GRUModel 86 | _model->init(model_path, model_input_sizes, 87 | n_outputs, n_state); 88 | _config.skipInference = old_skip_switch_val; 89 | } -------------------------------------------------------------------------------- /src/GuiItems/ModelComboBox.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: ModelComboBox.cpp 36 | Implements ModelComboBox GUI object. 37 | */ 38 | #include "GuiItems.hpp" 39 | 40 | ModelComboBoxItem::ModelComboBoxItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node): foleys::GuiItem (builder, node) 41 | { 42 | addAndMakeVisible (combobox); 43 | } 44 | 45 | // Sets combobox callback and updates model list. 46 | void ModelComboBoxItem::update() 47 | { 48 | //std::cout << "[MODEL CBOX] Update()" << std::endl; 49 | if (auto* processor = dynamic_cast(magicBuilder.getMagicState().getProcessor())) 50 | { 51 | combobox.onChange = [&] { 52 | if (auto* proc = dynamic_cast(magicBuilder.getMagicState().getProcessor())) 53 | { 54 | const auto selected_entry = combobox.getSelectedId() - 1; 55 | // Stop Audio Processing Thread ( It can crash when re-loading model ) 56 | proc->suspendProcessing(true); 57 | proc->reload_model(selected_entry); 58 | proc->updateKnobs(); 59 | proc->storeToValTree(ValTree_IDs::gui_params,"SelectedID",juce::var(combobox.getSelectedId())); 60 | proc->suspendProcessing(false); 61 | 62 | auto *status_label = magicBuilder.findGuiItemWithId("lbl_status"); 63 | if(status_label) status_label->update(); 64 | } 65 | 66 | }; 67 | combobox.clear(false); // Remove all previous items 68 | int menu_idx = 1; 69 | auto *guiconfig = magicBuilder.getMagicState().getObjectWithType("guiconfig"); 70 | if(!guiconfig) return; 71 | // Update ComboBox entries 72 | for (std::string menuentry : guiconfig->modelnames) 73 | { 74 | combobox.addItem(menuentry, menu_idx); 75 | menu_idx++; 76 | } 77 | // Fetch Selected ID from ValTree 78 | auto child_tree = magicBuilder.getMagicState().getValueTree(). 79 | getChildWithName(ValTree_IDs::gui_params); 80 | if(child_tree.isValid()) 81 | { 82 | int selected_id = child_tree.getProperty("SelectedID"); 83 | combobox.setSelectedId(selected_id,juce::dontSendNotification); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/FMSynth/FMSynth.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: FMSynth.hpp 36 | 37 | 6-Operator FM synth implementation 38 | */ 39 | 40 | #ifndef SRC_FMSYNTH_HPP_ 41 | #define SRC_FMSYNTH_HPP_ 42 | #endif // SRC_FMSYNTH_HPP_ 43 | 44 | #include 45 | #include 46 | #include 47 | 48 | #include "algorithms.hpp" 49 | 50 | class FMSynth { 51 | public: 52 | FMSynth(); 53 | ~FMSynth(); 54 | 55 | void init(float sampleRate, int blockSize); 56 | void set_ratios(std::array fr_coarse, std::array fr_fine); 57 | void set_config(unsigned int config); 58 | unsigned int get_config(); 59 | std::array get_fr_fine(); 60 | std::array get_fr_coarse(); 61 | float* render(float pitch_hz, std::vector ol); 62 | void load_dx7_config(const std::array patch); 63 | 64 | private: 65 | void update_phase(float pitch_hz); 66 | void reset_phase(); 67 | void render_mm(float pitch_hz, std::array inc_ol); 68 | void fade_out(); 69 | void fade_in(); 70 | 71 | std::array _phase; 72 | std::array _prev_ol; 73 | std::array _fr; 74 | std::array _fr_fine; 75 | std::array _fr_coarse; 76 | float _previous_pitch_hz; 77 | unsigned int _config; 78 | int _block_size; 79 | float _t_step; 80 | std::vector _buffer; 81 | 82 | int _modmatrix[36] = {0}; 83 | float _outmatrix[6] = {0}; 84 | 85 | std::array _mm_table = { 86 | ALG1_MM, ALG2_MM, ALG3_MM, ALG4_MM, ALG5_MM, ALG6_MM, ALG7_MM, 87 | ALG8_MM, ALG9_MM, ALG10_MM, ALG11_MM, ALG12_MM, ALG13_MM, ALG14_MM, 88 | ALG15_MM, ALG16_MM, ALG17_MM, ALG18_MM, ALG19_MM, ALG20_MM, ALG21_MM, 89 | ALG22_MM, ALG23_MM, ALG24_MM, ALG25_MM, ALG26_MM, ALG27_MM, ALG28_MM, 90 | ALG29_MM, ALG30_MM, ALG31_MM, ALG32_MM}; 91 | std::array _outm_table = { 92 | ALG1_OUTM, ALG2_OUTM, ALG3_OUTM, ALG4_OUTM, ALG5_OUTM, ALG6_OUTM, 93 | ALG7_OUTM, ALG8_OUTM, ALG9_OUTM, ALG10_OUTM, ALG11_OUTM, ALG12_OUTM, 94 | ALG13_OUTM, ALG14_OUTM, ALG15_OUTM, ALG16_OUTM, ALG17_OUTM, ALG18_OUTM, 95 | ALG19_OUTM, ALG20_OUTM, ALG21_OUTM, ALG22_OUTM, ALG23_OUTM, ALG24_OUTM, 96 | ALG25_OUTM, ALG26_OUTM, ALG27_OUTM, ALG28_OUTM, ALG29_OUTM, ALG30_OUTM, 97 | ALG31_OUTM, ALG32_OUTM, 98 | }; 99 | }; 100 | -------------------------------------------------------------------------------- /src/PluginConfig.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: PluginConfig.hpp 36 | Declares config structures to store data related to plugin operation and GUI. 37 | */ 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | struct PluginGUIConfig 46 | { 47 | std::vector modelfilenames; //Valid model filename list 48 | std::vector modelnames; //Valid modelname list 49 | std::vector nstates; //Valid model statewidth list 50 | std::string modeldir = "~/"; //Directory listed for models 51 | std::string status = ""; //Message from Status Label 52 | 53 | // FM Synth attributes for GUI display 54 | unsigned int fm_algo = 0; 55 | std::array fm_coarse = {1,1,1,1,1,1}; //FM Ratios Coarse 56 | std::array fm_fine = {0}; //FM Ratios Fine 57 | 58 | }; 59 | 60 | struct PluginConfig 61 | { 62 | int num_fmblocks; // How many 64-sample FM blocks we fit in an audio block. 63 | float pitch_ratio; 64 | float yin_threshold; 65 | float rms_clamp_value; 66 | float feedback_level; 67 | float in_gain; 68 | float out_gain; 69 | bool portamento; 70 | bool infinite_sustain; 71 | bool allow_model_reset; 72 | bool enableOSCOutput; 73 | 74 | // FM Synth config 75 | unsigned int fm_config = 0; 76 | std::array fm_coarse = {1,1,1,1,1,1}; //FM Ratios Coarse 77 | std::array fm_fine = {0}; //FM Ratios Fine 78 | std::array fm_boost = 79 | {1.0f,1.0f,1.0f,1.0f,1.0f,1.0f}; //Osc Amplitude Boost 80 | 81 | 82 | //Function switches 83 | bool enableConsoleOutput; 84 | bool skipInference; 85 | bool enableAudioPassthrough; 86 | 87 | PluginConfig() 88 | { 89 | num_fmblocks = 0; 90 | pitch_ratio = 1.0f; 91 | yin_threshold = 0.15f; 92 | feedback_level = 0.0f; 93 | in_gain = 1.0f; 94 | out_gain = 1.0f; 95 | portamento = false; 96 | infinite_sustain = false; 97 | allow_model_reset = true; 98 | enableOSCOutput = true; 99 | enableConsoleOutput = false; 100 | skipInference = false; 101 | enableAudioPassthrough = false; 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /src/FeatureProcessing/RMSProcessor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: RMSProcessor.cpp 36 | Computes block-wise input audio RMS over a window. 37 | */ 38 | 39 | #include "RMSProcessor.hpp" 40 | #include 41 | #include 42 | #include 43 | 44 | 45 | constexpr float _REF_DB = 20.0f; //maximum reached by input (will be mapped to 1.0) 46 | constexpr float _DB_RANGE = 70.0f; //minimum where pitch is tracked (will be mapped to 0.0) 47 | constexpr float a_rms = 1.0f/(_DB_RANGE-_REF_DB); 48 | constexpr float b_rms = _DB_RANGE/(_DB_RANGE-_REF_DB); 49 | 50 | RMS_Processor::~RMS_Processor() 51 | { 52 | free(_rms_buffer); 53 | } 54 | 55 | void RMS_Processor::init(int windowSize, int blockSize, bool linear_output) 56 | { 57 | std::cout << "[RMS] Called init winsize " << windowSize << " blocksize " << blockSize << std::endl; 58 | _n_entries = windowSize/blockSize; 59 | _rms_buffer = (float*) malloc( _n_entries * sizeof(float)); 60 | _use_linear_output = linear_output; 61 | for(int i = 0; i < _n_entries;i++) 62 | _rms_buffer[i] = 0.0f; 63 | _measure_count = 0; 64 | _block_size = blockSize; 65 | } 66 | 67 | 68 | float RMS_Processor::process(const float* frame) 69 | { 70 | /*Measure frame's RMS */ 71 | float retval = 0.0f; 72 | float acc = 0.0f; 73 | for(int i = 0; i < _block_size;i++) 74 | { 75 | acc += frame[i]*frame[i]; 76 | } 77 | acc /= (float)_block_size; 78 | /*Accumulate in buffer */ 79 | _rms_buffer[_measure_count] = acc; 80 | _measure_count = ++_measure_count & (_n_entries-1); //Wrap counter 81 | 82 | /* Fetch complete RMS window */ 83 | acc = 0.0f; 84 | for(int i = 0; i < _n_entries;i++) 85 | acc += _rms_buffer[i]; 86 | acc /= (float)_n_entries; 87 | 88 | if(_use_linear_output == true) 89 | { // Linear RMS to amp feature, clamp at 2. 90 | const float lim = 2.0f; 91 | acc = acc * 32.0f; // Found by trial and error. 92 | retval = acc > lim? lim : acc; 93 | } 94 | else 95 | { // Logarithm RMS to amp feature 96 | /* Compute Squared RMS in dB */ 97 | if (acc < 1e-20f) acc = 1e-20f; //Avoid log instabilities 98 | float rms_squared_db = 20.0*log10f(acc); 99 | //std::cout << "acc = " << acc << " rms_squared_db = " << rms_squared_db << std::endl; 100 | 101 | /* Normalize */ 102 | // rms_norm = a * rms + b; -> a = 1/60, b = 4/3 103 | if(rms_squared_db < -_DB_RANGE) rms_squared_db = -_DB_RANGE; 104 | 105 | retval = (a_rms * rms_squared_db + b_rms); 106 | } 107 | return retval; 108 | } -------------------------------------------------------------------------------- /src/Inference/EnvModels.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: EnvModels.cpp 36 | */ 37 | 38 | #include "EnvModels.hpp" 39 | 40 | #include 41 | 42 | /* 43 | GRU based model 44 | Retains state internally - only requires the current conditioning value 45 | */ 46 | 47 | std::vector GRUModel::call(float pitch, float loudness) { 48 | std::array inbuffer; 49 | inbuffer[0] = normalize_pitch(pitch); 50 | inbuffer[1] = normalize_loudness(loudness); 51 | 52 | if (_isStandalone) 53 | _torchmodel->call(inbuffer, _outbuffer); 54 | else 55 | _torchmodel->call(inbuffer, _outbuffer, _statebuffer); 56 | 57 | return _outbuffer; 58 | } 59 | 60 | std::vector GRUModel::get_state() { return _statebuffer; } 61 | 62 | void GRUModel::init(const std::string &filename, 63 | std::array model_input_sizes, int n_outputs) { 64 | init(filename, model_input_sizes, n_outputs, 0); 65 | } 66 | 67 | bool GRUModel::reset_state() { 68 | if (_isStandalone == true) 69 | return false; 70 | else 71 | std::fill(_statebuffer.begin(), _statebuffer.end(), 0.0f); 72 | return true; 73 | } 74 | 75 | void GRUModel::init(const std::string &filename, 76 | std::array model_input_sizes, int n_outputs, 77 | int n_state) { 78 | InferenceConfig config; 79 | config.filename = filename; 80 | config.fixed_slices = {0, 0, 0}; 81 | config.input_sizes = model_input_sizes; 82 | config.output_len = n_outputs; 83 | config.rolling_slice = 2; 84 | config.state_len = n_state; 85 | _isStandalone = (n_state > 0) ? false : true; 86 | _torchmodel->init(config); 87 | if (_torchmodel->contains_patch()) { 88 | _containsPatch = true; 89 | _torchmodel->get_patch(_initial_patch); 90 | } 91 | _outbuffer.resize(n_outputs); 92 | _statebuffer.resize(n_state); 93 | } 94 | 95 | EnvModel::EnvModel() { _torchmodel.reset(new TorchModel()); } 96 | 97 | EnvModel::~EnvModel() {} 98 | 99 | inline float EnvModel::normalize_pitch(float pitch) { 100 | if (pitch < 20.0f) return 0.0f; 101 | constexpr float midi_highest_note = 127.0f; 102 | constexpr float log_two = 0.69314718056f; 103 | const float midi_val = 12 * (logf(pitch / 220.0f) / log_two) + 57.01f; 104 | return midi_val / midi_highest_note; 105 | } 106 | 107 | inline float EnvModel::normalize_loudness(float loudness) { 108 | constexpr float _REF_DB = 20.7f; 109 | constexpr float _DB_RANGE = 80.0f; 110 | if (loudness < 1e-20f) loudness = 1e-20f; // Avoid log instabilities 111 | float ld_squared_db = 20.0 * log10f(loudness); 112 | // printf("acc = %f rms_squared_db = %f \n",acc,rms_squared_db); 113 | /* Normalize */ 114 | ld_squared_db -= _REF_DB; 115 | if (ld_squared_db < -_DB_RANGE) ld_squared_db = -_DB_RANGE; 116 | 117 | /* Scale [-DB_RANGE, 0] to [0, 1]. */ 118 | return (ld_squared_db / _DB_RANGE) + 1.0f; 119 | } 120 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | jobs: 11 | ubuntu_build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Install needed dependencies 15 | run: sudo apt-get update && sudo apt-get install -y git cmake g++ libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libfreetype-dev libcurl4-openssl-dev libasound2-dev 16 | env: 17 | DEBIAN_FRONTEND: noninteractive 18 | 19 | - name: Clone the repository 20 | uses: actions/checkout@v3 21 | with: 22 | submodules: recursive 23 | 24 | - name: Setup torch 25 | run: | 26 | curl -L https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-2.0.0%2Bcpu.zip -o libtorch.zip 27 | unzip libtorch.zip 28 | 29 | - name: Setup the build 30 | run: | 31 | mkdir -p build 32 | cd build 33 | cmake .. -DCMAKE_BUILD_TYPE=Release 34 | 35 | - name: Build 36 | working-directory: build 37 | run: cmake --build . --config Release -j 4 38 | 39 | - name: Upload build artifacts 40 | uses: actions/upload-artifact@v3 41 | with: 42 | name: BesselsTrick_Ubuntu 43 | path: build/BesselsTrick_artefacts/Release 44 | 45 | macos_arm64_build: 46 | runs-on: macOS-latest 47 | steps: 48 | - name: Clone the repository 49 | uses: actions/checkout@v3 50 | with: 51 | submodules: recursive 52 | 53 | - name: Import Certificates 54 | uses: apple-actions/import-codesign-certs@v2 55 | with: 56 | p12-file-base64: ${{ secrets.DEV_ID_APP_CERT }} 57 | p12-password: ${{ secrets.DEV_ID_APP_PASSWORD }} 58 | 59 | - name: Setup torch 60 | run: | 61 | curl -L https://anaconda.org/pytorch/pytorch/2.0.0/download/osx-arm64/pytorch-2.0.0-py3.9_0.tar.bz2 -o pytorch.tar.bz2 62 | mkdir pytorch && tar -xvf pytorch.tar.bz2 -C pytorch 63 | mv ./pytorch/lib/python3.9/site-packages/torch ./libtorch 64 | rm -rf pytorch 65 | 66 | - name: Setup the build 67 | run: | 68 | mkdir -p build 69 | cd build 70 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 71 | 72 | - name: Build 73 | working-directory: build 74 | run: cmake --build . --config Release -j 75 | 76 | - name: Codesign app bundle 77 | working-directory: build 78 | run: | 79 | echo "Run codesign" 80 | /usr/bin/codesign --force -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" -v ./BesselsTrick_artefacts/Release/AU/BesselsTrick.component --deep --strict --options=runtime --timestamp -v 81 | /usr/bin/codesign --force -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" -v ./BesselsTrick_artefacts/Release/VST3/BesselsTrick.vst3 --deep --strict --options=runtime --timestamp -v 82 | 83 | echo "Creating temp notarization archive" 84 | zip -r plugin.zip ./BesselsTrick_artefacts/Release/AU/BesselsTrick.component ./BesselsTrick_artefacts/Release/VST3/BesselsTrick.vst3 85 | 86 | echo "Notarize" 87 | xcrun notarytool submit plugin.zip --apple-id "${{ secrets.APPLE_ID}}" --password "${{ secrets.NOTARIZE_PASSWORD}}" --team-id "${{ secrets.TEAM_ID}}" --wait 88 | 89 | echo "Attach staples" 90 | xcrun stapler staple ./BesselsTrick_artefacts/Release/VST3/BesselsTrick.vst3 91 | xcrun stapler staple ./BesselsTrick_artefacts/Release/AU/BesselsTrick.component 92 | rm ./BesselsTrick_artefacts/Release/libBesselsTrick_SharedCode.a 93 | 94 | - name: Upload build artifacts 95 | uses: actions/upload-artifact@v3 96 | with: 97 | name: BesselsTrick_MacOS_arm64 98 | path: build/BesselsTrick_artefacts/Release 99 | 100 | 101 | macos_intel_build: 102 | runs-on: macOS-latest 103 | steps: 104 | - name: Clone the repository 105 | uses: actions/checkout@v3 106 | with: 107 | submodules: recursive 108 | 109 | - name: Import Certificates 110 | uses: apple-actions/import-codesign-certs@v2 111 | with: 112 | p12-file-base64: ${{ secrets.DEV_ID_APP_CERT }} 113 | p12-password: ${{ secrets.DEV_ID_APP_PASSWORD }} 114 | 115 | - name: Setup torch 116 | run: | 117 | curl -L https://download.pytorch.org/libtorch/cpu/libtorch-macos-2.0.0.zip -o libtorch.zip 118 | unzip libtorch.zip 119 | 120 | - name: Setup the build 121 | run: | 122 | mkdir -p build 123 | cd build 124 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 125 | 126 | - name: Build 127 | working-directory: build 128 | run: cmake --build . --config Release -j 129 | 130 | - name: Codesign app bundle 131 | working-directory: build 132 | run: | 133 | echo "Run codesign" 134 | /usr/bin/codesign --force -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" -v ./BesselsTrick_artefacts/Release/AU/BesselsTrick.component --deep --strict --options=runtime --timestamp -v 135 | /usr/bin/codesign --force -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" -v ./BesselsTrick_artefacts/Release/VST3/BesselsTrick.vst3 --deep --strict --options=runtime --timestamp -v 136 | 137 | echo "Creating temp notarization archive" 138 | zip -r plugin.zip ./BesselsTrick_artefacts/Release/AU/BesselsTrick.component ./BesselsTrick_artefacts/Release/VST3/BesselsTrick.vst3 139 | 140 | echo "Notarize" 141 | xcrun notarytool submit plugin.zip --apple-id "${{ secrets.APPLE_ID}}" --password "${{ secrets.NOTARIZE_PASSWORD}}" --team-id "${{ secrets.TEAM_ID}}" --wait 142 | 143 | echo "Attach staples" 144 | xcrun stapler staple ./BesselsTrick_artefacts/Release/VST3/BesselsTrick.vst3 145 | xcrun stapler staple ./BesselsTrick_artefacts/Release/AU/BesselsTrick.component 146 | rm ./BesselsTrick_artefacts/Release/libBesselsTrick_SharedCode.a 147 | 148 | - name: Upload build artifacts 149 | uses: actions/upload-artifact@v3 150 | with: 151 | name: BesselsTrick_MacOS_intel 152 | path: build/BesselsTrick_artefacts/Release 153 | -------------------------------------------------------------------------------- /src/PluginProcessor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: PluginProcessor.hpp 36 | Plugin Processor Class (BesselsProcessor) declaration 37 | */ 38 | 39 | #pragma once 40 | 41 | #include 42 | #include 43 | #include 44 | 45 | #include "BinaryData.h" 46 | #include "Inference/EnvModels.hpp" 47 | #include "FMSynth/FMSynth.hpp" 48 | #include "FeatureProcessing/FeatureRegister.hpp" 49 | #include "FeatureProcessing/RMSProcessor.hpp" 50 | #include "FeatureProcessing/Yin.hpp" 51 | #include "FeatureProcessing/PitchTrackManager.hpp" 52 | #include "PluginConfig.hpp" 53 | #include "ParameterIDs.hpp" 54 | 55 | 56 | // Move to static method 57 | juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); 58 | 59 | //============================================================================== 60 | class BesselsProcessor : public foleys::MagicProcessor, 61 | private juce::AudioProcessorValueTreeState::Listener { 62 | public: 63 | //============================================================================== 64 | BesselsProcessor(); 65 | ~BesselsProcessor() override; 66 | 67 | //============================================================================== 68 | void prepareToPlay(double sampleRate, int samplesPerBlock) override; 69 | void releaseResources() override; 70 | 71 | bool isBusesLayoutSupported(const BusesLayout& layouts) const override; 72 | 73 | void processBlock(juce::AudioBuffer&, juce::MidiBuffer&) override; 74 | 75 | //============================================================================== 76 | const juce::String getName() const override; 77 | 78 | bool acceptsMidi() const override; 79 | bool producesMidi() const override; 80 | bool isMidiEffect() const override; 81 | double getTailLengthSeconds() const override; 82 | //============================================================================== 83 | int getNumPrograms() override; 84 | int getCurrentProgram() override; 85 | void setCurrentProgram(int index) override; 86 | const juce::String getProgramName(int index) override; 87 | void changeProgramName(int index, const juce::String& newName) override; 88 | void sendDebugMessages(int fmblock, float pitch, float pitch_norm, float rms_in, std::vector fm_ol); 89 | void initialiseBuilder(foleys::MagicGUIBuilder& builder) override; 90 | void parameterChanged(const juce::String& param, float value) override; 91 | void postSetStateInformation () override; 92 | void storeToValTree(juce::Identifier child_id,juce::String property,juce::var value); 93 | void configure_gui_listeners(); 94 | void add_triggers(); 95 | void printValueTree(); 96 | void showLoadDialog(); 97 | void loadModelList(); 98 | void setupMeters(); 99 | void setupValueTree(); 100 | void updateKnob(juce::String knob_id, double value); 101 | void updateKnobs(); 102 | void updateMeters(float rms_in, float pitch,juce::AudioBuffer &buffer); 103 | void updateGuiConfig(); 104 | 105 | /* Model Routine Functions*/ 106 | void load_gru_model( 107 | const std::string& model_filename); // For standalone gru models 108 | void load_gru_model(const std::string& model_path, int n_state); 109 | void apply_config(); 110 | void reload_model(const unsigned int entry); 111 | 112 | 113 | /* Application Specific attributes. */ 114 | juce::AudioProcessorValueTreeState treeState; // Plugin Tree State 115 | juce::AudioBuffer _fm_render_buffer; // Render buffer 116 | juce::AudioProcessLoadMeasurer _load_measurer; // CPU Load measurer 117 | std::unique_ptr _fmsynth; // Synth. 118 | std::unique_ptr _model; // Resynthesis Model wrapper pointer. 119 | PitchTrackManager<4> _tracker_manager; // Pitch tracker manager 120 | std::unique_ptr _rms_processor; // RMS Processor 121 | PluginConfig _config; // Config structure 122 | juce::OSCSender _osc_sender; // OSC IF 123 | FeatureRegister _feat_register; // Feature Register 124 | 125 | private: 126 | 127 | const int fm_block_size = 64; // Internal. RNN renders at 44.1kHz with block size of 64. 128 | juce::AudioFormatManager manager; 129 | 130 | // Workaround to fetch MagicGUIBuilder from MagicPlugin class without 131 | // overriding createEditor() (which in practice should not be declared by 132 | // plugin) 133 | foleys::MagicGUIBuilder* builder_ptr = 0; 134 | foleys::MagicLevelSource* _input_rms_meter{nullptr}; 135 | foleys::MagicLevelSource* _input_f0_meter{nullptr}; 136 | foleys::MagicLevelSource* _output_meter{nullptr}; 137 | juce::LookAndFeel_V1 plotLookAndFeel; 138 | //============================================================================== 139 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BesselsProcessor) 140 | }; 141 | -------------------------------------------------------------------------------- /src/FeatureProcessing/Yin.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | File: YIN.cpp 3 | 4 | adapted by Franco Caspe for block-based operation. 5 | Adapted from: https://github.com/JorenSix/Pidato 6 | 7 | Original project license: GNU LESSER GENERAL PUBLIC LICENSE 8 | Version 3, 29 June 2007 9 | 10 | */ 11 | #include "Yin.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | void Yin::init(float yinSampleRate, int yinBufferSize, int frameSize, 18 | float yinThreshold, bool downsample_x2) { 19 | _downsample_x2 = downsample_x2; 20 | const int downsampleFactor = _downsample_x2 ? 2 : 1; 21 | _sampleRate = yinSampleRate / downsampleFactor; 22 | _bufferSize = yinBufferSize; 23 | _frameSize = frameSize; 24 | _halfBufferSize = _bufferSize / 2; 25 | _probability = 0.0; 26 | _writeIdx = 0; 27 | _input_gain = 1.0f; 28 | 29 | // Counter that specifies how many frames to take before 30 | // starting to compute yin 31 | if ((_frameSize / downsampleFactor) < yinBufferSize) 32 | _fillCounter = (yinBufferSize / (_frameSize / downsampleFactor)) - 1; 33 | else 34 | _fillCounter = 0; // A single frame will fill yin buffer. 35 | 36 | // std::cout << "[DEBUG] YIN frame size " << _frameSize << " buffer size " << 37 | // _bufferSize << std::endl; initialize array and set it to zero 38 | free(_audioBuffer); 39 | _audioBuffer = (float *)malloc(sizeof(float) * _bufferSize); 40 | for (int i = 0; i < _bufferSize; i++) { 41 | _audioBuffer[i] = 0; 42 | } 43 | free(_yinBuffer); 44 | _yinBuffer = (float *)malloc(sizeof(float) * _halfBufferSize); 45 | for (int i = 0; i < _halfBufferSize; i++) { 46 | _yinBuffer[i] = 0; 47 | } 48 | 49 | setThreshold(yinThreshold); // Default is 0.15 50 | } 51 | 52 | Yin::Yin() { 53 | _audioBuffer = (float *)NULL; 54 | _yinBuffer = (float *)NULL; 55 | } 56 | 57 | Yin::~Yin() { 58 | free(_audioBuffer); 59 | free(_yinBuffer); 60 | } 61 | 62 | float Yin::getProbability() { return _probability; } 63 | 64 | float Yin::getPitch() { 65 | // Make sure we have some data in the audio buffer 66 | float acc = 0; 67 | for (int i = 0; i < _bufferSize; i++) { 68 | acc += fabs(_audioBuffer[i]); 69 | } 70 | if (acc < 0.01f) return -1; 71 | 72 | if (_fillCounter != 0) { 73 | _fillCounter--; 74 | return -1; 75 | } 76 | 77 | int tauEstimate = -1; 78 | float pitchInHertz = 0; 79 | 80 | // step 2 81 | difference(); 82 | 83 | // step 3 84 | cumulativeMeanNormalizedDifference(); 85 | 86 | // step 4 87 | tauEstimate = absoluteThreshold(); 88 | 89 | // step 5 90 | if (tauEstimate != -1) { 91 | pitchInHertz = _sampleRate / parabolicInterpolation(tauEstimate); 92 | } 93 | 94 | return pitchInHertz; 95 | } 96 | 97 | void Yin::setThreshold(float threshold) { _threshold = threshold; } 98 | 99 | float Yin::parabolicInterpolation(int tauEstimate) { 100 | float betterTau; 101 | int x0; 102 | int x2; 103 | 104 | if (tauEstimate < 1) { 105 | x0 = tauEstimate; 106 | } else { 107 | x0 = tauEstimate - 1; 108 | } 109 | if (tauEstimate + 1 < _halfBufferSize) { 110 | x2 = tauEstimate + 1; 111 | } else { 112 | x2 = tauEstimate; 113 | } 114 | if (x0 == tauEstimate) { 115 | if (_yinBuffer[tauEstimate] <= _yinBuffer[x2]) { 116 | betterTau = tauEstimate; 117 | } else { 118 | betterTau = x2; 119 | } 120 | } else if (x2 == tauEstimate) { 121 | if (_yinBuffer[tauEstimate] <= _yinBuffer[x0]) { 122 | betterTau = tauEstimate; 123 | } else { 124 | betterTau = x0; 125 | } 126 | } else { 127 | float s0, s1, s2; 128 | s0 = _yinBuffer[x0]; 129 | s1 = _yinBuffer[tauEstimate]; 130 | s2 = _yinBuffer[x2]; 131 | // fixed AUBIO implementation, thanks to Karl Helgason: 132 | // (2.0f * s1 - s2 - s0) was incorrectly multiplied with -1 133 | betterTau = tauEstimate + (s2 - s0) / (2 * (2 * s1 - s2 - s0)); 134 | } 135 | return betterTau; 136 | } 137 | 138 | void Yin::cumulativeMeanNormalizedDifference() { 139 | int tau; 140 | _yinBuffer[0] = 1; 141 | float runningSum = 0; 142 | for (tau = 1; tau < _halfBufferSize; tau++) { 143 | runningSum += _yinBuffer[tau]; 144 | _yinBuffer[tau] *= tau / runningSum; 145 | } 146 | } 147 | 148 | void Yin::updateBuffer(const float *frame) { 149 | // std::cout << "\nupdate buffer - framesize " << _frameSize << std::endl; 150 | // std::cout << "\n writeidx start: " << _writeIdx << std::endl; 151 | const int downsampleFactor = _downsample_x2 ? 2 : 1; 152 | for (int i = 0; i < _frameSize/downsampleFactor; i++) { 153 | const int idx = (i + _writeIdx) % _bufferSize; 154 | 155 | if(_downsample_x2) 156 | _audioBuffer[idx] = _input_gain * (frame[2*i] + frame[2*i+1]) / 2.0f; 157 | else 158 | _audioBuffer[idx] = _input_gain * frame[i]; 159 | // std::cout << idx << " "; 160 | // std::cout << _audioBuffer[idx] << " "; 161 | } 162 | _writeIdx = (_writeIdx + (_frameSize/downsampleFactor)) % _bufferSize; 163 | // std::cout << "\n writeidx end: " << _writeIdx << std::endl; 164 | } 165 | 166 | void Yin::difference() { 167 | int index; 168 | int tau; 169 | float delta; 170 | // std::cout << "\n\ndifference\n"; 171 | for (tau = 0; tau < _halfBufferSize; tau++) { 172 | // std::cout << "\ndly " << tau << std::endl; 173 | for (index = 0; index < _halfBufferSize; index++) { 174 | /*Compute indexes for circular buffer.*/ 175 | const int idx = (index + _writeIdx) % _bufferSize; 176 | const int idx_plus_tau = (idx + tau) % _bufferSize; 177 | // std::cout << " " << idx << " - " << idx_plus_tau << " "; 178 | // std::cout << "(" << _audioBuffer[idx] << ")" << "[" << 179 | // _audioBuffer[idx_plus_tau] << "] "; 180 | delta = _audioBuffer[idx] - _audioBuffer[idx_plus_tau]; 181 | _yinBuffer[tau] += delta * delta; 182 | } 183 | // std::cout << _yinBuffer[tau] << "-- "; 184 | } 185 | } 186 | 187 | int Yin::absoluteThreshold() { 188 | int tau; 189 | // first two positions in yinBuffer are always 1 190 | // So start at the third (index 2) 191 | // std::cout << "\nabs threshold\n"; 192 | for (tau = 2; tau < _halfBufferSize; tau++) { 193 | // std::cout << _yinBuffer[tau] << " "; 194 | if (_yinBuffer[tau] < _threshold) { 195 | while (tau + 1 < _halfBufferSize && 196 | _yinBuffer[tau + 1] < _yinBuffer[tau]) { 197 | tau++; 198 | } 199 | // found tau, exit loop and return 200 | // store the probability 201 | // From the YIN paper: The threshold determines the list of 202 | // candidates admitted to the set, and can be interpreted as the 203 | // proportion of aperiodic power tolerated 204 | // within a ëëperiodicíí signal. 205 | // 206 | // Since we want the periodicity and and not aperiodicity: 207 | // periodicity = 1 - aperiodicity 208 | _probability = 1 - _yinBuffer[tau]; 209 | break; 210 | } 211 | } 212 | // if no pitch found, tau => -1 213 | if (tau == _halfBufferSize || _yinBuffer[tau] >= _threshold) { 214 | tau = -1; 215 | _probability = 0; 216 | } 217 | return tau; 218 | } 219 | -------------------------------------------------------------------------------- /resources/magic2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | 27 | 29 | 32 | 35 | 36 | 38 | 39 | 43 | 48 | 51 | 52 | 56 | 59 | 60 | 61 | 62 | 63 | 65 | 66 | 67 | 68 | 69 | 71 | 72 | 73 | 74 | 75 | 77 | 78 | 79 | 80 | 81 | 83 | 84 | 85 | 86 | 87 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/GuiItems/GuiItems.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: GuiItems.hpp 36 | 37 | Declaration of Special GUI items, including: 38 | DrawableLabel - for displaying FM algorithm. 39 | ModelComboBox - for listing patches. 40 | StatusBar - for printing indications of use in GUI. 41 | RatiosBar - for printing oscillator frequency ratios in GUI. 42 | */ 43 | 44 | // Avoid using namespace juce, as it collides with libtorch. 45 | #define DONT_SET_USING_JUCE_NAMESPACE 1 46 | #include "JuceHeader.h" 47 | #include 48 | #include "../PluginProcessor.hpp" 49 | 50 | // gui component for displaying the FM algorithm 51 | class DrawableLabel : public juce::Component 52 | { 53 | public: 54 | enum ColourIDs 55 | { 56 | // we are safe from collissions, because we set the colours on every component directly from the stylesheet 57 | backgroundColourId, 58 | drawColourId, 59 | fillColourId 60 | }; 61 | 62 | DrawableLabel(); 63 | void setAlgorithm(int number); 64 | void paint (juce::Graphics& g) override; 65 | 66 | private: 67 | int _algorithm = 1; 68 | const char *_algoplots[32] = { 69 | BinaryData::algo_1_png,BinaryData::algo_2_png,BinaryData::algo_3_png,BinaryData::algo_4_png, 70 | BinaryData::algo_5_png,BinaryData::algo_6_png,BinaryData::algo_7_png,BinaryData::algo_8_png, 71 | BinaryData::algo_9_png,BinaryData::algo_10_png,BinaryData::algo_11_png,BinaryData::algo_12_png, 72 | BinaryData::algo_13_png,BinaryData::algo_14_png,BinaryData::algo_15_png,BinaryData::algo_16_png, 73 | BinaryData::algo_17_png,BinaryData::algo_18_png,BinaryData::algo_19_png,BinaryData::algo_20_png, 74 | BinaryData::algo_21_png,BinaryData::algo_22_png,BinaryData::algo_23_png,BinaryData::algo_24_png, 75 | BinaryData::algo_25_png,BinaryData::algo_26_png,BinaryData::algo_27_png,BinaryData::algo_28_png, 76 | BinaryData::algo_29_png,BinaryData::algo_30_png,BinaryData::algo_31_png,BinaryData::algo_32_png 77 | }; 78 | 79 | const int _algoplotSizes[32] = { 80 | BinaryData::algo_1_pngSize,BinaryData::algo_2_pngSize,BinaryData::algo_3_pngSize,BinaryData::algo_4_pngSize, 81 | BinaryData::algo_5_pngSize,BinaryData::algo_6_pngSize,BinaryData::algo_7_pngSize,BinaryData::algo_8_pngSize, 82 | BinaryData::algo_9_pngSize,BinaryData::algo_10_pngSize,BinaryData::algo_11_pngSize,BinaryData::algo_12_pngSize, 83 | BinaryData::algo_13_pngSize,BinaryData::algo_14_pngSize,BinaryData::algo_15_pngSize,BinaryData::algo_16_pngSize, 84 | BinaryData::algo_17_pngSize,BinaryData::algo_18_pngSize,BinaryData::algo_19_pngSize,BinaryData::algo_20_pngSize, 85 | BinaryData::algo_21_pngSize,BinaryData::algo_22_pngSize,BinaryData::algo_23_pngSize,BinaryData::algo_24_pngSize, 86 | BinaryData::algo_25_pngSize,BinaryData::algo_26_pngSize,BinaryData::algo_27_pngSize,BinaryData::algo_28_pngSize, 87 | BinaryData::algo_29_pngSize,BinaryData::algo_30_pngSize,BinaryData::algo_31_pngSize,BinaryData::algo_32_pngSize 88 | }; 89 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableLabel) 90 | }; 91 | 92 | // DrawableLabel: Shows current FM synth algorithm 93 | class DrawableLabelItem : public foleys::GuiItem, juce::Timer 94 | { 95 | public: 96 | FOLEYS_DECLARE_GUI_FACTORY (DrawableLabelItem) 97 | 98 | DrawableLabelItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node); 99 | void update() override; 100 | void timerCallback() override; 101 | juce::Component* getWrappedComponent() override 102 | { 103 | return &drawablelabel; 104 | } 105 | 106 | private: 107 | DrawableLabel drawablelabel; 108 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableLabelItem) 109 | }; 110 | 111 | 112 | // ModelComboBox: Displays a list of models to load. 113 | class ModelComboBoxItem : public foleys::GuiItem 114 | { 115 | public: 116 | FOLEYS_DECLARE_GUI_FACTORY (ModelComboBoxItem) 117 | 118 | ModelComboBoxItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node); 119 | // Sets combobox callback and updates model list. 120 | void update() override; 121 | juce::Component* getWrappedComponent() override 122 | { 123 | return &combobox; 124 | } 125 | 126 | private: 127 | juce::ComboBox combobox; 128 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModelComboBoxItem) 129 | }; 130 | 131 | 132 | // Status Bar, provides indications to users 133 | class StatusBarItem : public foleys::GuiItem 134 | { 135 | public: 136 | FOLEYS_DECLARE_GUI_FACTORY (StatusBarItem) 137 | 138 | StatusBarItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node); 139 | void update_status_message(); 140 | // Sets label callback and updates model list. 141 | void update() override; 142 | juce::Component* getWrappedComponent() override 143 | { 144 | return &label; 145 | } 146 | 147 | private: 148 | juce::Label label; 149 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusBarItem) 150 | }; 151 | 152 | 153 | // This class is creating and configuring your custom component 154 | class RatiosBarItem : public foleys::GuiItem, 155 | private juce::Timer 156 | { 157 | public: 158 | FOLEYS_DECLARE_GUI_FACTORY (RatiosBarItem) 159 | 160 | RatiosBarItem (foleys::MagicGUIBuilder& builder, const juce::ValueTree& node); 161 | void refresh(); 162 | std::vector getSettableProperties() const override; 163 | void timerCallback() override; 164 | // Sets label callback and updates model list. 165 | void update() override; 166 | juce::Component* getWrappedComponent() override 167 | { 168 | return &label; 169 | } 170 | 171 | private: 172 | juce::Label label; 173 | 174 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RatiosBarItem) 175 | }; -------------------------------------------------------------------------------- /src/FMSynth/FMSynth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: FMSynth.cpp 36 | 37 | 6-Operator FM synth implementation 38 | */ 39 | #include "FMSynth.hpp" 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #define OP6 5 46 | #define OP5 4 47 | #define OP4 3 48 | #define OP3 2 49 | #define OP2 1 50 | #define OP1 0 51 | 52 | constexpr float TWO_PI = 2 * 3.14159265f; 53 | 54 | FMSynth::FMSynth() { 55 | _config = 0; 56 | _t_step = 0; 57 | _block_size = 0; 58 | _previous_pitch_hz = 0; 59 | set_config(1); // Config for String Synth 60 | } 61 | FMSynth::~FMSynth() { 62 | // free(_buffer); //No need. Vector deletes itself when out of scope. 63 | } 64 | 65 | void FMSynth::load_dx7_config(const std::array patch) { 66 | std::cout << "FMSynth::load_dx7_config()" << std::endl; 67 | uint8_t algorithm = patch[134]; // 0 - 31 68 | set_config(algorithm); 69 | for (int op = 0; op < 6; op++) { 70 | // First in patch is OP6 71 | const int offset = op * 21; 72 | uint8_t is_fixed = patch[offset + 17]; 73 | uint8_t f_coarse = patch[offset + 18]; 74 | uint8_t f_fine = patch[offset + 19]; 75 | uint8_t f_detune = patch[offset + 20]; 76 | _fr_coarse[5-op] = f_coarse; 77 | _fr_fine[5-op] = f_fine; 78 | 79 | // TODO: Add detune parameter +- 7 cents. 80 | float f = (f_coarse == 0) ? 0.5f : (float)f_coarse; 81 | _fr[5 - op] = f + (f / 100) * ((float)f_fine); 82 | // std::cout << "coarse " << (int)f_coarse << " fine " << (int)f_fine << " 83 | // total: "<< _fr[5-op] << std::endl; 84 | } 85 | std::cout << "\t alg: " << (uint16_t)algorithm << " fr: " << _fr[0] << " " 86 | << _fr[1] << " " << _fr[2] << " " << _fr[3] << " " << _fr[4] << " " 87 | << _fr[5] << std::endl; 88 | _config = algorithm; 89 | // uint8_t transpose = (patch[144]-24); 90 | // double factor = 2^(((double)transpose)/12.0); 91 | // fr = factor*fr 92 | return; 93 | } 94 | 95 | void FMSynth::set_ratios(std::array fr_coarse, 96 | std::array fr_fine) 97 | { 98 | /* Load frequency rations */ 99 | for (int op = 0; op < 6; op++) { 100 | _fr_coarse[op] = fr_coarse[op]; 101 | _fr_fine[op] = fr_fine[op]; 102 | 103 | // TODO: Add detune parameter +- 7 cents. 104 | float f = (fr_coarse[op] == 0) ? 0.5f : (float)fr_coarse[op]; 105 | _fr[op] = f + (f / 100) * ((float)fr_fine[op]); 106 | } 107 | } 108 | 109 | void FMSynth::set_config(unsigned int config) { 110 | if (config > 31) { 111 | throw("FMSynth::set_config - Invalid config value."); 112 | } 113 | memcpy(_outmatrix, _outm_table[config], sizeof(float) * 6); 114 | memcpy(_modmatrix, _mm_table[config], sizeof(int) * 36); 115 | 116 | // Now, normalize outmatrix values. 117 | float outval = 0.0f; 118 | for (int i = 0; i < 6; i++) outval += _outmatrix[i]; 119 | 120 | outval = outval * 2; // Max OL is 2 in dx7 121 | 122 | for (int i = 0; i < 6; i++) _outmatrix[i] /= outval; 123 | _config = config; // Store value 124 | } 125 | 126 | unsigned int FMSynth::get_config() { return _config; } 127 | 128 | std::array FMSynth::get_fr_coarse() { return _fr_coarse; } 129 | std::array FMSynth::get_fr_fine() { return _fr_fine; } 130 | 131 | void FMSynth::init(float sampleRate, int blockSize) { 132 | _buffer.resize(blockSize); 133 | for (int i = 0; i < 6; i++) _prev_ol[i] = 0.0f; 134 | _block_size = blockSize; 135 | _t_step = 1.0f / sampleRate; 136 | _previous_pitch_hz = 0.0f; 137 | reset_phase(); 138 | } 139 | 140 | void FMSynth::reset_phase() { 141 | for (int i = 0; i < 6; i++) _phase[i] = 0.0f; 142 | } 143 | 144 | inline void FMSynth::fade_out() { 145 | float ramp = 1.0f; 146 | const float ramp_step = 1.0f / ((float)_block_size); 147 | for (int s = 0; s < _buffer.size(); s++) { 148 | _buffer[s] = _buffer[s] * ramp; // Linear Fade 149 | //_buffer[s] = _buffer[s]* (ramp*ramp); //Concave quadratic fade. 150 | ramp -= ramp_step; 151 | } 152 | } 153 | 154 | inline void FMSynth::fade_in() { 155 | float ramp = 0.0f; 156 | const float ramp_step = 1.0f / ((float)_block_size); 157 | for (int s = 0; s < _buffer.size(); s++) { 158 | _buffer[s] = _buffer[s] * ramp; // Linear Fade 159 | //_buffer[s] = _buffer[s]* (ramp * ramp); //Concave quadratic Fade 160 | ramp += ramp_step; 161 | } 162 | } 163 | 164 | inline void FMSynth::update_phase(float pitch_hz) { 165 | /* Update Phase accumulator */ 166 | for (int i = 0; i < _phase.size(); i++) { 167 | _phase[i] += TWO_PI * pitch_hz * _fr[i] * _t_step; 168 | if (_phase[i] > TWO_PI) { 169 | _phase[i] -= TWO_PI; 170 | } 171 | } 172 | } 173 | 174 | float* FMSynth::render(float pitch_hz, std::vector ol) { 175 | /* Compute ol increment for linear interpolation */ 176 | std::array inc_ol; 177 | for (int i = 0; i < _prev_ol.size(); i++) 178 | inc_ol[i] = (ol[i] - _prev_ol[i]) / (float)(_block_size); 179 | 180 | /* Render silence if no freq info has been capture over two runs. */ 181 | if (pitch_hz < 1.0f && _previous_pitch_hz < 1.0f) { 182 | std::fill(_buffer.begin(), _buffer.end(), 0.0f); 183 | reset_phase(); 184 | } 185 | 186 | // if pitch_hz > 1 OR pitch_hz < 1 && _prev_pitch > 1 187 | else { 188 | // Select wether to render the last block's pitch (for fading out) of 189 | // current block. 190 | float used_pitch_hz = (pitch_hz < 1.0f) ? _previous_pitch_hz : pitch_hz; 191 | 192 | render_mm(used_pitch_hz, inc_ol); 193 | 194 | // If this is the first valid pitch value 195 | if (_previous_pitch_hz < 1.0f) { 196 | fade_in(); 197 | } 198 | } 199 | 200 | /* If previous pitch was valid but now it's silence, fade out. */ 201 | if (pitch_hz < 1.0f) { 202 | fade_out(); 203 | reset_phase(); 204 | } 205 | 206 | /* Store previous pitch and ol values. */ 207 | _previous_pitch_hz = pitch_hz; 208 | std::copy_n(ol.begin(), _prev_ol.size(), _prev_ol.begin()); //_prev_ol = ol; 209 | return _buffer.data(); 210 | } 211 | 212 | /* Render FM using modulation matrix */ 213 | void FMSynth::render_mm(float pitch_hz, std::array inc_ol) { 214 | std::array ol; 215 | ol = _prev_ol; 216 | const float scale = TWO_PI; // standard scale for DX ( OLs go up to 2.0 ) 217 | 218 | /*Plain auto is by value (you get a copy). Use auto&.*/ 219 | for (auto& sample : _buffer) { 220 | // Create instant phase modulation registers 221 | std::array modphases; 222 | modphases = _phase; 223 | 224 | // Iterate through modulation matrix 225 | for (int mod_op = OP6; mod_op >= OP1; mod_op--) { 226 | for (int carr_op = OP1; carr_op <= OP6; carr_op++) { 227 | int mm_idx = carr_op * 6 + mod_op; // modmatrix index 228 | // Select modulatior OPS 229 | if (_modmatrix[mm_idx]) { 230 | // std::cout << "Carrier OP" << carr_op+1 << " modulated by OP" << 231 | // mod_op+1 << std::endl; 232 | const auto mod_ol = ol[mod_op]; 233 | const float mod_output_to_carrier = 234 | sinf(modphases[mod_op]) * mod_ol * scale; 235 | modphases[carr_op] += mod_output_to_carrier; 236 | } 237 | } 238 | } 239 | 240 | /* Store sample - should use transform and a lambda expresion */ 241 | sample = 0.0f; 242 | for (int i = OP1; i <= OP6; i++) 243 | sample += _outmatrix[i] * ol[i] * sinf(modphases[i]); 244 | 245 | /* Update phase and ol */ 246 | update_phase(pitch_hz); 247 | 248 | // ol += inc_ol; 249 | std::transform(ol.begin(), ol.end(), inc_ol.begin(), ol.begin(), 250 | std::plus()); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/Inference/TorchInference.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | ============================================================================== 4 | Copyright (c) 2023 Franco Caspe 5 | All rights reserved. 6 | 7 | **BSD 3-Clause License** 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 3. Neither the name of the copyright holder nor the names of its contributors 17 | may be used to endorse or promote products derived from this software without 18 | specific prior written permission. 19 | 20 | ============================================================================== 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 30 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 31 | OF THE POSSIBILITY OF SUCH DAMAGE. 32 | ============================================================================== 33 | */ 34 | 35 | /* 36 | File: TorchInference.cpp 37 | Implements inference and patch retrieval methods on TorchScript models. 38 | */ 39 | 40 | #include "TorchInference.hpp" 41 | 42 | #include 43 | 44 | TorchModel::TorchModel() {} 45 | 46 | void TorchModel::init(InferenceConfig config) { 47 | _config = config; 48 | _isTypeES = (_config.state_len == 0) ? false : true; 49 | { 50 | torch::NoGradGuard no_guard; // Will only disable grads in current thread. 51 | try { 52 | // Deserialize the ScriptModule from a file using torch::jit::load(). 53 | _module = torch::jit::load(_config.filename.c_str()); 54 | } catch (const c10::Error &e) { 55 | std::cerr << "[LIBTORCH MODULE] error loading the model " 56 | << _config.filename << std::endl; 57 | return; 58 | } 59 | 60 | const std::string base_filename = 61 | _config.filename.substr(_config.filename.find_last_of("/\\") + 1); 62 | std::cout << "[LIBTORCH MODULE] " << base_filename << " loaded!\n"; 63 | 64 | std::cout << "\tInput Size: [" << _config.input_sizes[0] << ", " 65 | << _config.input_sizes[1] << ", " << _config.input_sizes[2] << "]" 66 | << '\n'; 67 | 68 | std::vector inputs; 69 | inputs.push_back(torch::ones({_config.input_sizes[0], _config.input_sizes[1], 70 | _config.input_sizes[2]})); 71 | 72 | if (_isTypeES) 73 | // TODO: Not sure this works like so or we need an ivalue::Tuple 74 | inputs.push_back(torch::zeros({1, 1, config.state_len})); 75 | 76 | // Execute the model 77 | auto outputs = _module.forward(inputs); 78 | 79 | // Process its output. 80 | if (_isTypeES) // Model is exposed state 81 | { 82 | // Output is an ivalue::tuple 83 | at::Tensor output = outputs.toTuple()->elements()[0].toTensor(); 84 | std::cout << "\tOutput Size: [" << output.size(0) << ", " << output.size(1) 85 | << ", " << output.size(2) << "]" << '\n'; 86 | 87 | at::Tensor state = outputs.toTuple()->elements()[1].toTensor(); 88 | std::cout << "\tState Size: [" << state.size(0) << ", " << state.size(1) 89 | << ", " << state.size(2) << "]" << '\n'; 90 | 91 | // std::cout << "[ES GRU] Model Output is tuple " << outputs.isTuple() << 92 | // std::endl; std::cout << "[ES GRU] Print output[0]: " << 93 | // outputs.toTuple()->elements()[0].toTensor() << std::endl; std::cout << 94 | // "[ES GRU] Print output[1]: " << 95 | // outputs.toTuple()->elements()[1].toTensor() << std::endl; 96 | } else // model is standalone 97 | { 98 | at::Tensor output = outputs.toTensor(); 99 | 100 | std::cout << "\tOutput Size: [" << output.size(0) << ", " << output.size(1) 101 | << ", " << output.size(2) << "]" << '\n'; 102 | } 103 | } 104 | } 105 | 106 | bool TorchModel::contains_patch() { 107 | // Check if the module contains a training patch in buffer. 108 | const int n_buffers = _module.buffers().size(); 109 | std::cerr << "[LIBTORCH MODULE] Module buffer count:" << n_buffers 110 | << std::endl; 111 | if (n_buffers > 0) return true; 112 | 113 | return false; 114 | } 115 | 116 | void TorchModel::get_patch(std::array &dest) { 117 | const int n_buffers = _module.buffers().size(); 118 | if (n_buffers > 0) { 119 | //Print tensor 120 | //std::cout << " First buffer:\n" 121 | // << *(_module.buffers().begin()) << std::endl; 122 | const int tensor_len = (*(_module.buffers().begin())).size(0); 123 | uint8_t *patch = (*_module.buffers().begin()).data_ptr(); 124 | std::copy(patch, patch + tensor_len, dest.begin()); 125 | //std::cout << "\nCopied buffer: ["; 126 | //for (uint8_t val : dest) std::cout << " " << static_cast(val); 127 | //std::cout << " ]\n"; 128 | } 129 | } 130 | /* 131 | Execute a forward pass for a standalone model 132 | */ 133 | void TorchModel::call(std::array input_array, 134 | std::vector &output_array) { 135 | { 136 | torch::NoGradGuard no_guard; // Will only disable grads in current thread. 137 | /* Exposes the given data as a Tensor without taking ownership of the original 138 | * data. */ 139 | at::Tensor input_tensor = torch::from_blob( 140 | input_array.data(), 141 | {_config.input_sizes[0], _config.input_sizes[1], _config.input_sizes[2]}); 142 | 143 | // std::cout << "Input "<< input_tensor << '\n'; 144 | std::vector inputs; 145 | inputs.push_back(input_tensor); 146 | 147 | // Execute the model and turn its output (which is another jit::IValue) into a 148 | // tensor! 149 | at::Tensor output = _module.forward(inputs).toTensor(); 150 | auto out_a = output.accessor(); 151 | std::array access_indexes = _config.fixed_slices; 152 | 153 | for (int i = 0; i < output.size(_config.rolling_slice); i++) { 154 | /* Do not apply sigmoid activation and multiply by max_ol. 155 | This should be done within the torchscript now. */ 156 | 157 | access_indexes[_config.rolling_slice] = i; 158 | output_array[i] = 159 | out_a[access_indexes[0]][access_indexes[1]][access_indexes[2]]; 160 | } 161 | } 162 | } 163 | 164 | /* 165 | Execute a forward pass for an exposed state model 166 | */ 167 | void TorchModel::call(std::array input_array, 168 | std::vector &output_array, 169 | std::vector &state_array) { 170 | // std::cout << "[DEBUG] Running call in ES model." << std::endl; 171 | 172 | { 173 | torch::NoGradGuard no_guard; // Will only disable grads in current thread. 174 | /* Exposes the given data as a Tensor without taking ownership of the original 175 | * data. */ 176 | at::Tensor input_tensor = torch::from_blob( 177 | input_array.data(), 178 | {_config.input_sizes[0], _config.input_sizes[1], _config.input_sizes[2]}); 179 | 180 | at::Tensor state_tensor = torch::from_blob( 181 | state_array.data(), 182 | {_config.input_sizes[0], _config.input_sizes[1], _config.state_len}); 183 | 184 | // std::cout << "Input "<< input_tensor << '\n'; 185 | // std::cout << "State "<< state_tensor << '\n'; 186 | // TODO: Not sure this will work like so, or if we require to create an 187 | // ivalue::Tuple. 188 | std::vector inputs; 189 | inputs.push_back(input_tensor); 190 | inputs.push_back(state_tensor); 191 | 192 | torch::jit::IValue outputs; 193 | outputs = _module.forward(inputs); 194 | 195 | // TODO: Correct this. just placeholder. 196 | // Good info here: 197 | // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/core/ivalue.h 198 | at::Tensor model_out = outputs.toTuple()->elements()[0].toTensor(); 199 | at::Tensor model_state = outputs.toTuple()->elements()[1].toTensor(); 200 | 201 | // Copy output to vector 202 | auto out_a = model_out.accessor(); 203 | std::array access_indexes = _config.fixed_slices; 204 | 205 | for (int i = 0; i < model_out.size(_config.rolling_slice); i++) { 206 | /* Do not apply sigmoid activation and multiply by max_ol. 207 | This should be done within the torchscript now. */ 208 | 209 | access_indexes[_config.rolling_slice] = i; 210 | output_array[i] = 211 | out_a[access_indexes[0]][access_indexes[1]][access_indexes[2]]; 212 | } 213 | 214 | // Copy state to vector 215 | out_a = model_state.accessor(); 216 | for (int i = 0; i < model_state.size(_config.rolling_slice); i++) { 217 | access_indexes[_config.rolling_slice] = i; 218 | state_array[i] = 219 | out_a[access_indexes[0]][access_indexes[1]][access_indexes[2]]; 220 | } 221 | } 222 | } -------------------------------------------------------------------------------- /src/FMSynth/algorithms.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: algorithms.hpp 36 | Defines oscillator routing for FM modulation. 37 | */ 38 | 39 | #ifndef SRC_ALGORITHMS_HPP_ 40 | #define SRC_ALGORITHMS_HPP_ 41 | #endif // SRC_ALGORITHMS_HPP_ 42 | 43 | const int ALG1_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 45 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 46 | const float ALG1_OUTM[6] = {1, 0, 1, 0, 0, 0}; 47 | const int ALG2_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 49 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 50 | const float ALG2_OUTM[6] = {1, 0, 1, 0, 0, 0}; 51 | const int ALG3_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 52 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 53 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 54 | const float ALG3_OUTM[6] = {1, 0, 0, 1, 0, 0}; 55 | const int ALG4_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 56 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 57 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 58 | const float ALG4_OUTM[6] = {1, 0, 0, 1, 0, 0}; 59 | const int ALG5_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 61 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 62 | const float ALG5_OUTM[6] = {1, 0, 1, 0, 1, 0}; 63 | const int ALG6_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 65 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 66 | const float ALG6_OUTM[6] = {1, 0, 1, 0, 1, 0}; 67 | const int ALG7_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68 | 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 69 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 70 | const float ALG7_OUTM[6] = {1, 0, 1, 0, 0, 0}; 71 | const int ALG8_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72 | 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 73 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 74 | const float ALG8_OUTM[6] = {1, 0, 1, 0, 0, 0}; 75 | const int ALG9_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76 | 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 77 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 78 | const float ALG9_OUTM[6] = {1, 0, 1, 0, 0, 0}; 79 | const int ALG10_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 80 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 81 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 82 | const float ALG10_OUTM[6] = {1, 0, 0, 1, 0, 0}; 83 | const int ALG11_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 84 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 85 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 86 | const float ALG11_OUTM[6] = {1, 0, 0, 1, 0, 0}; 87 | const int ALG12_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88 | 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 89 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 90 | const float ALG12_OUTM[6] = {1, 0, 1, 0, 0, 0}; 91 | const int ALG13_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92 | 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 93 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 94 | const float ALG13_OUTM[6] = {1, 0, 1, 0, 0, 0}; 95 | const int ALG14_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 97 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 98 | const float ALG14_OUTM[6] = {1, 0, 1, 0, 0, 0}; 99 | const int ALG15_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 101 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 102 | const float ALG15_OUTM[6] = {1, 0, 1, 0, 0, 0}; 103 | const int ALG16_MM[36] = {0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 104 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 105 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 106 | const float ALG16_OUTM[6] = {1, 0, 0, 0, 0, 0}; 107 | const int ALG17_MM[36] = {0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 108 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 109 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 110 | const float ALG17_OUTM[6] = {1, 0, 0, 0, 0, 0}; 111 | const int ALG18_MM[36] = {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 112 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 113 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 114 | const float ALG18_OUTM[6] = {1, 0, 0, 0, 0, 0}; 115 | const int ALG19_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 116 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 117 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 118 | const float ALG19_OUTM[6] = {1, 0, 0, 1, 1, 0}; 119 | const int ALG20_MM[36] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 120 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 121 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 122 | const float ALG20_OUTM[6] = {1, 1, 0, 1, 0, 0}; 123 | const int ALG21_MM[36] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 124 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 125 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 126 | const float ALG21_OUTM[6] = {1, 1, 0, 1, 1, 0}; 127 | const int ALG22_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 129 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 130 | const float ALG22_OUTM[6] = {1, 0, 1, 1, 1, 0}; 131 | const int ALG23_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 132 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 133 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 134 | const float ALG23_OUTM[6] = {1, 1, 0, 1, 1, 0}; 135 | const int ALG24_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 137 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 138 | const float ALG24_OUTM[6] = {1, 1, 1, 1, 1, 0}; 139 | const int ALG25_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 141 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 142 | const float ALG25_OUTM[6] = {1, 1, 1, 1, 1, 0}; 143 | const int ALG26_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 144 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 145 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 146 | const float ALG26_OUTM[6] = {1, 1, 0, 1, 0, 0}; 147 | const int ALG27_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 148 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 149 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 150 | const float ALG27_OUTM[6] = {1, 1, 0, 1, 0, 0}; 151 | const int ALG28_MM[36] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 153 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 154 | const float ALG28_OUTM[6] = {1, 0, 1, 0, 0, 1}; 155 | const int ALG29_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 157 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 158 | const float ALG29_OUTM[6] = {1, 1, 1, 0, 1, 0}; 159 | const int ALG30_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160 | 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 161 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 162 | const float ALG30_OUTM[6] = {1, 1, 1, 0, 0, 1}; 163 | const int ALG31_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165 | 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; 166 | const float ALG31_OUTM[6] = {1, 1, 1, 1, 1, 0}; 167 | const int ALG32_MM[36] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 170 | const float ALG32_OUTM[6] = {1, 1, 1, 1, 1, 1}; 171 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Bessels Trick CMakeLists.txt 2 | 3 | # To get started on a new plugin, copy this entire folder (containing this file and C++ sources) to 4 | # a convenient location, and then start making modifications. 5 | 6 | # The first line of any CMake project should be a call to `cmake_minimum_required`, which checks 7 | # that the installed CMake will be able to understand the following CMakeLists, and ensures that 8 | # CMake's behaviour is compatible with the named version. This is a standard CMake command, so more 9 | # information can be found in the CMake docs. 10 | 11 | cmake_minimum_required(VERSION 3.14) 12 | # The top-level CMakeLists.txt file for a project must contain a literal, direct call to the 13 | # `project()` command. `project()` sets up some helpful variables that describe source/binary 14 | # directories, and the current project version. This is a standard CMake command. 15 | 16 | project(BesselsTrick VERSION 0.0.1) 17 | set(CMAKE_CXX_STANDARD 17) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | 20 | if(APPLE) 21 | set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0.1") 22 | set (formats AU VST3) 23 | else() 24 | set (formats VST3) 25 | endif() 26 | 27 | option(JUCE_BUILD_EXTRAS "Build JUCE Extras" OFF) 28 | 29 | # If you've installed JUCE somehow (via a package manager, or directly using the CMake install 30 | # target), you'll need to tell this project that it depends on the installed copy of JUCE. If you've 31 | # included JUCE directly in your source tree (perhaps as a submodule), you'll need to tell CMake to 32 | # include that subdirectory as part of the build. 33 | 34 | # find_package(JUCE CONFIG REQUIRED) # If you've installed JUCE to your system 35 | # or 36 | # add_subdirectory(JUCE) # If you've put JUCE in a subdirectory called JUCE 37 | 38 | include(FetchContent) 39 | FetchContent_Declare( 40 | juce 41 | GIT_REPOSITORY https://github.com/juce-framework/JUCE.git 42 | GIT_TAG 69795dc) 43 | 44 | FetchContent_MakeAvailable(juce) 45 | 46 | FetchContent_Declare( 47 | foleys_gui_magic 48 | GIT_REPOSITORY https://github.com/ffAudio/foleys_gui_magic 49 | GIT_TAG 795f879) 50 | 51 | # Make the content available for the plugin, don't build it. 52 | # We just add the module directory. 53 | FetchContent_Populate(foleys_gui_magic) 54 | add_subdirectory(${foleys_gui_magic_SOURCE_DIR}/modules) 55 | 56 | ##### Torch related 57 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 58 | include(TorchUtils) 59 | # build.sh leaves libtorch in ./libtorch/ - Fetch from there. 60 | list(APPEND CMAKE_PREFIX_PATH "${PROJECT_SOURCE_DIR}/libtorch") 61 | find_package(Torch REQUIRED) 62 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}") 63 | 64 | 65 | # Test RPATH 66 | set(CMAKE_MACOSX_RPATH TRUE) 67 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) 68 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # This is to use the use the local libraries in the rpath as well 69 | # set(CMAKE_SKIP_BUILD_RPATH FALSE) 70 | set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE) 71 | 72 | if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") 73 | # This is the path to the folder containing the executable 74 | set(CMAKE_INSTALL_RPATH "@loader_path") 75 | elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") 76 | set(CMAKE_INSTALL_RPATH "$ORIGIN") 77 | endif() 78 | 79 | 80 | # Experimental: Compile with -fPIC for linking VST in Linux 81 | if (UNIX AND NOT APPLE) 82 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") 83 | endif() 84 | 85 | # `juce_add_plugin` adds a static library target with the name passed as the first argument 86 | # (BesselsTrick here). This target is a normal CMake target, but has a lot of extra properties set 87 | # up by default. As well as this shared code static library, this function adds targets for each of 88 | # the formats specified by the FORMATS arguments. This function accepts many optional arguments. 89 | # Check the readme at `docs/CMake API.md` in the JUCE repo for the full list. 90 | 91 | juce_add_plugin(BesselsTrick 92 | # VERSION ... # Set this if the plugin version is different to the project version 93 | # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone 94 | # ICON_SMALL ... 95 | # IS_SYNTH TRUE/FALSE # Is this a synth or an effect? 96 | # NEEDS_MIDI_INPUT TRUE/FALSE # Does the plugin need midi input? 97 | # NEEDS_MIDI_OUTPUT TRUE/FALSE # Does the plugin need midi output? 98 | # IS_MIDI_EFFECT TRUE/FALSE # Is this plugin a MIDI effect? 99 | # EDITOR_WANTS_KEYBOARD_FOCUS TRUE/FALSE # Does the editor need keyboard focus? 100 | # COPY_PLUGIN_AFTER_BUILD TRUE/FALSE # Should the plugin be installed to a default location after building? 101 | COMPANY_NAME AIL # Specify the name of the plugin's author 102 | PLUGIN_MANUFACTURER_CODE Fran # A four-character manufacturer id with at least one upper-case character 103 | PLUGIN_CODE FMTT # A unique four-character plugin id with at least one upper-case character 104 | FORMATS ${formats} # The formats to build. Other valid formats are: AAX Unity VST AU AUv3 105 | PRODUCT_NAME "BesselsTrick") # The name of the final executable, which can differ from the target name 106 | 107 | # `juce_generate_juce_header` will create a JuceHeader.h for a given target, which will be generated 108 | # into your build tree. This should be included with `#include `. The include path for 109 | # this header will be automatically added to the target. The main function of the JuceHeader is to 110 | # include all your JUCE module headers; if you're happy to include module headers directly, you 111 | # probably don't need to call this. 112 | 113 | juce_generate_juce_header(BesselsTrick) 114 | 115 | # `target_sources` adds source files to a target. We pass the target that needs the sources as the 116 | # first argument, then a visibility parameter for the sources (PRIVATE is normally best practice, 117 | # although it doesn't really affect executable targets). Finally, we supply a list of source files 118 | # that will be built into the target. This is a standard CMake command. 119 | 120 | target_sources(BesselsTrick PRIVATE 121 | src/PluginProcessor.cpp 122 | src/PluginProcessorGUI.cpp 123 | src/PluginProcessorModelRoutines.cpp 124 | src/Inference/TorchInference.cpp 125 | src/Inference/EnvModels.cpp 126 | src/FMSynth/FMSynth.cpp 127 | src/FeatureProcessing/RMSProcessor.cpp 128 | src/FeatureProcessing/Yin.cpp 129 | src/GuiItems/DrawableLabel.cpp 130 | src/GuiItems/ModelComboBox.cpp 131 | src/GuiItems/RatiosBar.cpp 132 | src/GuiItems/StatusBar.cpp 133 | ) 134 | 135 | # `target_compile_definitions` adds some preprocessor definitions to our target. In a Projucer 136 | # project, these might be passed in the 'Preprocessor Definitions' field. JUCE modules also make use 137 | # of compile definitions to switch certain features on/off, so if there's a particular feature you 138 | # need that's not on by default, check the module header for the correct flag to set here. These 139 | # definitions will be visible both to your code, and also the JUCE module code, so for new 140 | # definitions, pick unique names that are unlikely to collide! This is a standard CMake command. 141 | 142 | target_compile_definitions(BesselsTrick 143 | PRIVATE 144 | # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them. 145 | FOLEYS_ENABLE_BINARY_DATA=1 146 | FOLEYS_SHOW_GUI_EDITOR_PALLETTE=0 # Disables the PGM floating WYSWYG editor. 147 | JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call 148 | JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call 149 | JUCE_VST3_CAN_REPLACE_VST2=0) 150 | 151 | # If your target needs extra binary assets, you can add them here. The first argument is the name of 152 | # a new static library target that will include all the binary resources. There is an optional 153 | # `NAMESPACE` argument that can specify the namespace of the generated binary data class. Finally, 154 | # the SOURCES argument should be followed by a list of source files that should be built into the 155 | # static library. These source files can be of any kind (wav data, images, fonts, icons etc.). 156 | # Conversion to binary-data will happen when your target is built. 157 | 158 | juce_add_binary_data(${PROJECT_NAME}_data 159 | SOURCES 160 | resources/magic_bake.xml resources/img/logo.png resources/img/bgcolor.png 161 | resources/img/algo_1.png resources/img/algo_2.png resources/img/algo_3.png resources/img/algo_4.png 162 | resources/img/algo_5.png resources/img/algo_6.png resources/img/algo_7.png resources/img/algo_8.png 163 | resources/img/algo_9.png resources/img/algo_10.png resources/img/algo_11.png resources/img/algo_12.png 164 | resources/img/algo_13.png resources/img/algo_14.png resources/img/algo_15.png resources/img/algo_16.png 165 | resources/img/algo_17.png resources/img/algo_18.png resources/img/algo_19.png resources/img/algo_20.png 166 | resources/img/algo_21.png resources/img/algo_22.png resources/img/algo_23.png resources/img/algo_24.png 167 | resources/img/algo_25.png resources/img/algo_26.png resources/img/algo_27.png resources/img/algo_28.png 168 | resources/img/algo_29.png resources/img/algo_30.png resources/img/algo_31.png resources/img/algo_32.png) 169 | 170 | # `target_link_libraries` links libraries and JUCE modules to other libraries or executables. Here, 171 | # we're linking our executable target to the `juce::juce_audio_utils` module. Inter-module 172 | # dependencies are resolved automatically, so `juce_core`, `juce_events` and so on will also be 173 | # linked automatically. If we'd generated a binary data target above, we would need to link to it 174 | # here too. This is a standard CMake command. 175 | 176 | target_link_libraries(BesselsTrick PUBLIC 177 | ${PROJECT_NAME}_data #Binary data 178 | foleys_gui_magic 179 | juce_audio_processors 180 | juce::juce_osc 181 | juce::juce_recommended_config_flags 182 | juce::juce_recommended_lto_flags 183 | ${TORCH_LIBRARIES}) 184 | 185 | 186 | get_target_property(active_targets BesselsTrick JUCE_ACTIVE_PLUGIN_TARGETS) 187 | foreach( sub_target IN LISTS active_targets ) 188 | message(STATUS "Adding libraries to ${sub_target}") 189 | 190 | get_target_property( 191 | BUNDLE_PATH 192 | ${sub_target} 193 | JUCE_PLUGIN_ARTEFACT_FILE 194 | ) 195 | copy_torch_libs(${sub_target}) 196 | 197 | endforeach() 198 | 199 | juce_enable_copy_plugin_step(BesselsTrick) -------------------------------------------------------------------------------- /src/PluginProcessor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Copyright (c) 2023 Franco Caspe 4 | All rights reserved. 5 | 6 | **BSD 3-Clause License** 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. 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 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | ============================================================================== 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ============================================================================== 32 | */ 33 | 34 | /* 35 | File: PluginProcessor.cpp 36 | Implements main audio processing and audio configuration functions for the plguin. 37 | */ 38 | 39 | 40 | #include "PluginProcessor.hpp" 41 | #include 42 | 43 | /** 44 | BesselsProcessor(): Constructor 45 | Here we create our attribute objects. 46 | */ 47 | BesselsProcessor::BesselsProcessor() 48 | : foleys::MagicProcessor( 49 | juce::AudioProcessor::BusesProperties() 50 | .withInput("Input", juce::AudioChannelSet::stereo(), true) 51 | .withOutput("Output", juce::AudioChannelSet::stereo(), true)), 52 | treeState(*this, nullptr, "PARAMETERS", createParameterLayout()) 53 | 54 | { 55 | // Main Plugin Setup Tasks come here. 56 | 57 | // 1. Create new class members. 58 | //_pitch_tracker.reset(new Yin()); 59 | _tracker_manager.create(); 60 | _rms_processor.reset(new RMS_Processor()); 61 | //_rms_processor_feedback.reset(new RMS_Processor()); 62 | _fmsynth.reset(new FMSynth()); 63 | 64 | // 2. Set GUI 65 | FOLEYS_SET_SOURCE_PATH(__FILE__); 66 | add_triggers(); 67 | configure_gui_listeners(); 68 | setupMeters(); 69 | setupValueTree(); 70 | // juce::File gui_tree( 71 | // "/Users/franco/aim/projs/vst/ddx7-vst/resources/magic.xml"); 72 | // magicState.setGuiValueTree(gui_tree); 73 | magicState.setGuiValueTree (BinaryData::magic_bake_xml, 74 | BinaryData::magic_bake_xmlSize); 75 | 76 | // OSC Live Debug Module 77 | if (_osc_sender.connect("127.0.0.1", 12000)) // [4] 78 | std::cout << "[OSC] Error: could not connect to UDP port 12000." 79 | << std::endl; 80 | else 81 | std::cout << "[OSC] Connected: UDP port 12000." << std::endl; 82 | } 83 | 84 | /** 85 | BesselsProcessor(): Destructor 86 | Here we delete our attribute objects. 87 | */ 88 | BesselsProcessor::~BesselsProcessor() { 89 | /*Kill Renderer*/ 90 | _fmsynth.reset(); 91 | _model.reset(); 92 | _rms_processor.reset(); 93 | //_pitch_tracker.reset(); 94 | _tracker_manager.reset(); 95 | } 96 | 97 | inline float normalize_pitch(float pitch) { 98 | if (pitch < 20.0f) return 0.0f; 99 | constexpr float midi_highest_note = 127.0f; 100 | constexpr float log_two = 0.69314718056f; 101 | const float midi_val = 12 * (logf(pitch / 220.0f) / log_two) + 57.01f; 102 | return midi_val / midi_highest_note; 103 | } 104 | 105 | inline void BesselsProcessor::sendDebugMessages(int fmblock,float pitch, 106 | float pitch_norm,float rms_in, std::vector fm_ol) 107 | { 108 | /* DEBUG OVER CONSOLE */ 109 | if (_config.enableConsoleOutput == true) { 110 | std::cout << "[fmblock " << fmblock << "] [f0 " << pitch << "] [ld " << rms_in << "]\n"; 111 | for (int i = 0; i < 6; i++) std::cout << fm_ol[i] << " "; 112 | std::cout << std::endl; 113 | } 114 | 115 | /* Step7: Debug*/ 116 | if (_config.enableOSCOutput == true) { 117 | /* DEBUG OVER OSC */ 118 | juce::OSCMessage msg1("/f0ld"); 119 | msg1.addFloat32(pitch_norm); 120 | msg1.addFloat32(rms_in); 121 | msg1.addFloat32(0.0f); //Probability placeholder. Don't send anymore 122 | _osc_sender.send(msg1); 123 | 124 | // send envelopes over osc. 125 | juce::OSCMessage msg2("/fm_ol"); 126 | for (float value : fm_ol) msg2.addFloat32(value); 127 | _osc_sender.send(msg2); 128 | 129 | //if (_model) { 130 | // if (_model->is_standalone() == false) { 131 | // juce::OSCMessage msg3("/hidden"); 132 | // auto hidden_state = _model->get_state(); 133 | // for (float value : hidden_state) msg3.addFloat32(value); 134 | // _osc_sender.send(msg3); 135 | // } 136 | 137 | //juce::OSCMessage msg4("/fm_algo"); 138 | // msg4.addInt32(*_guiconfig.fm_algorithm); 139 | //_osc_sender.send(msg4); 140 | } 141 | } 142 | 143 | /** 144 | processBlock(): Rendering function 145 | 146 | The same buffer is used for audio input and output. 147 | 148 | */ 149 | void BesselsProcessor::processBlock(juce::AudioBuffer& buffer, 150 | juce::MidiBuffer& midiMessages) { 151 | /* Step1: Cleanup Tasks */ 152 | juce::ignoreUnused(midiMessages); 153 | 154 | juce::ScopedNoDenormals noDenormals; 155 | auto totalNumInputChannels = getTotalNumInputChannels(); 156 | auto totalNumOutputChannels = getTotalNumOutputChannels(); 157 | 158 | for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) 159 | buffer.clear(i, 0, buffer.getNumSamples()); 160 | 161 | juce::AudioProcessLoadMeasurer::ScopedTimer s(_load_measurer); 162 | juce::ignoreUnused(midiMessages); 163 | 164 | const int input_ch = 0; // Use Channel 0 as input 165 | 166 | // Compute global f0 for all fmblocks to be synthesized. 167 | _tracker_manager.updateBuffer(buffer.getReadPointer(input_ch)); 168 | float pitch = _tracker_manager.getPitch(); 169 | float pitch_norm = normalize_pitch(pitch); 170 | 171 | // Each fm block renders 64 samples, adapt to the selected plugin block size. 172 | for(int fmblock = 0; fmblock < _config.num_fmblocks ; fmblock++) 173 | { 174 | const int start_sample = fm_block_size*fmblock; 175 | 176 | auto* input_read_ptr = buffer.getWritePointer(input_ch,start_sample); 177 | auto *audio_input = buffer.getReadPointer(input_ch,start_sample); 178 | // Input Gain 179 | for (auto sample = 0; sample < fm_block_size; sample++) { 180 | input_read_ptr[sample] = input_read_ptr[sample] * _config.in_gain; 181 | } 182 | 183 | /* Step2: Gather control inputs */ 184 | // RMS 185 | float rms_in = _rms_processor->process(audio_input); 186 | 187 | // Run Feature Register 188 | rms_in = _feat_register.run(pitch, rms_in); 189 | 190 | /* Step3: DNN inference */ 191 | 192 | /* DNN Reset logic */ 193 | if(_config.allow_model_reset) 194 | if (rms_in <= 0.0 && pitch_norm <= 0.0) 195 | if (_config.skipInference == false && _model) { 196 | _model->reset_state(); 197 | } 198 | 199 | std::vector fm_ol; 200 | std::vector fm_ol_placeholder = {1, 0, 0, 0, 0, 0}; 201 | if (_model && _config.skipInference == false) { 202 | fm_ol = _model->call(pitch_norm, rms_in); 203 | } else 204 | fm_ol = fm_ol_placeholder; 205 | // FM Boost 206 | for (int i = 0; i < 6; i++) 207 | fm_ol[i] = fm_ol[i] * _config.fm_boost[i]; 208 | 209 | /* Step4: Render audio */ 210 | float* renderer_buffer = _fmsynth->render(pitch * _config.pitch_ratio, fm_ol); 211 | 212 | sendDebugMessages(fmblock,pitch,pitch_norm,rms_in,fm_ol); 213 | _fm_render_buffer.copyFrom(0, //Dest Channel 214 | start_sample, //Dest Start Sample 215 | renderer_buffer, // Source 216 | fm_block_size); //N Samples 217 | 218 | // If this is the last FM block, update GUI meters 219 | if(fmblock == _config.num_fmblocks - 1) 220 | updateMeters(rms_in,pitch,_fm_render_buffer); 221 | } // end for loop fmblock 222 | 223 | /* Step5: Store audio in JUCE's output buffers. */ 224 | for (int i = 0; i < totalNumOutputChannels; i++) { 225 | auto* channelData = buffer.getWritePointer(i); 226 | // Our fm render buffer contains just one channel 227 | auto *renderData = _fm_render_buffer.getReadPointer(0,0); 228 | if (_config.enableAudioPassthrough == false) { 229 | // Clear buffer with input audio. 230 | buffer.clear(i, 0, buffer.getNumSamples()); 231 | for (auto sample = 0; sample < buffer.getNumSamples(); sample++) { 232 | channelData[sample] = renderData[sample] * _config.out_gain; 233 | } 234 | } 235 | } 236 | 237 | } 238 | 239 | /** 240 | prepareToPlay(): Reset function to configure plugin according to audio driver. 241 | Here we will reset and config all our objects before rendering. 242 | */ 243 | void BesselsProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) { 244 | // Use this method as the place to do any pre-playback initialisation that you 245 | // need.. 246 | 247 | std::cout << "[DEBUG] prepareToPlay() called!" << std::endl; 248 | _load_measurer.reset(sampleRate, samplesPerBlock); 249 | 250 | _config.num_fmblocks = samplesPerBlock / fm_block_size; 251 | _fm_render_buffer.setSize(1,samplesPerBlock); 252 | /* Init renderer */ 253 | //_feedbackBuffer.resize(samplesPerBlock); 254 | if (_fmsynth) _fmsynth->init(sampleRate, fm_block_size); 255 | 256 | /* Init RMS processor */ 257 | const int RMS_WINDOW = 2048; 258 | if (_rms_processor) 259 | _rms_processor->init(RMS_WINDOW, // Window size 260 | fm_block_size, // Block size 261 | true); // Linear output 262 | 263 | /* Init Pitch Trackers */ 264 | // Minimum f0 detectable: 2*(sr/yinwindow) 265 | const std::array yin_windows = {256,512,1024,1280}; 266 | const std::array yin_downsample = {false,false,false,false}; 267 | _tracker_manager.init(sampleRate, yin_windows, samplesPerBlock, 268 | 0.15f, // Threshold 269 | yin_downsample); // Downsample x2 270 | } 271 | 272 | //============================================================================== 273 | const juce::String BesselsProcessor::getName() const { return JucePlugin_Name; } 274 | 275 | bool BesselsProcessor::acceptsMidi() const { 276 | #if JucePlugin_WantsMidiInput 277 | return true; 278 | #else 279 | return false; 280 | #endif 281 | } 282 | 283 | bool BesselsProcessor::producesMidi() const { 284 | #if JucePlugin_ProducesMidiOutput 285 | return true; 286 | #else 287 | return false; 288 | #endif 289 | } 290 | 291 | bool BesselsProcessor::isMidiEffect() const { 292 | #if JucePlugin_IsMidiEffect 293 | return true; 294 | #else 295 | return false; 296 | #endif 297 | } 298 | 299 | double BesselsProcessor::getTailLengthSeconds() const { return 0.0; } 300 | 301 | int BesselsProcessor::getNumPrograms() { 302 | return 1; // NB: some hosts don't cope very well if you tell them there are 0 303 | // programs, so this should be at least 1, even if you're not 304 | // really implementing programs. 305 | } 306 | 307 | int BesselsProcessor::getCurrentProgram() { return 0; } 308 | 309 | void BesselsProcessor::setCurrentProgram(int index) { juce::ignoreUnused(index); } 310 | 311 | const juce::String BesselsProcessor::getProgramName(int index) { 312 | juce::ignoreUnused(index); 313 | return {}; 314 | } 315 | 316 | void BesselsProcessor::changeProgramName(int index, const juce::String& newName) { 317 | juce::ignoreUnused(index, newName); 318 | } 319 | 320 | void BesselsProcessor::releaseResources() { 321 | // When playback stops, you can use this as an opportunity to free up any 322 | // spare memory, etc. 323 | } 324 | 325 | bool BesselsProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const { 326 | #if JucePlugin_IsMidiEffect 327 | juce::ignoreUnused(layouts); 328 | return true; 329 | #else 330 | // This is the place where you check if the layout is supported. 331 | // In this template code we only support mono or stereo. 332 | if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() && 333 | layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) 334 | return false; 335 | 336 | // This checks if the input layout matches the output layout 337 | #if !JucePlugin_IsSynth 338 | if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) 339 | return false; 340 | #endif 341 | 342 | return true; 343 | #endif 344 | } 345 | 346 | //============================================================================== 347 | // This creates new instances of the plugin.. 348 | juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() { 349 | return new BesselsProcessor(); 350 | } 351 | -------------------------------------------------------------------------------- /resources/v4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | 28 | 29 | 33 | 37 | 39 | 40 | 42 | 44 | 45 | 46 | 50 | 52 | 54 | 57 | 58 | 60 | 61 | 64 | 67 | 70 | 74 | 75 | 80 | 84 | 85 | 88 | 90 | 92 | 93 | 96 | 97 | 101 | 102 | 105 | 107 | 109 | 110 | 113 | 114 | 118 | 119 | 122 | 124 | 126 | 127 | 130 | 131 | 135 | 136 | 139 | 141 | 143 | 144 | 147 | 148 | 152 | 153 | 156 | 158 | 160 | 161 | 164 | 165 | 169 | 170 | 173 | 175 | 177 | 178 | 181 | 182 | 183 | 184 | 185 | 189 | 191 | 192 | 194 | 195 | 196 | 197 | 201 | 203 | 204 | 205 | 209 | 211 | 213 | 215 | 217 | 219 | 221 | 223 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /resources/magic.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | 28 | 30 | 31 | 34 | 36 | 37 | 42 | 46 | 48 | 49 | 51 | 53 | 54 | 55 | 59 | 61 | 63 | 66 | 67 | 69 | 70 | 73 | 76 | 79 | 83 | 84 | 89 | 93 | 94 | 97 | 99 | 101 | 102 | 104 | 105 | 109 | 110 | 113 | 115 | 117 | 118 | 120 | 121 | 125 | 126 | 129 | 131 | 133 | 134 | 136 | 137 | 141 | 142 | 145 | 147 | 149 | 150 | 152 | 153 | 157 | 158 | 161 | 163 | 165 | 166 | 168 | 169 | 173 | 174 | 177 | 179 | 181 | 182 | 184 | 185 | 186 | 187 | 188 | 192 | 194 | 195 | 197 | 198 | 199 | 200 | 204 | 206 | 207 | 208 | 212 | 214 | 216 | 218 | 220 | 222 | 224 | 226 | 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /resources/magic_bake.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | 28 | 30 | 31 | 34 | 36 | 37 | 42 | 46 | 48 | 49 | 51 | 53 | 54 | 55 | 59 | 61 | 63 | 66 | 67 | 69 | 70 | 73 | 76 | 79 | 83 | 84 | 89 | 93 | 94 | 97 | 99 | 101 | 102 | 104 | 105 | 109 | 110 | 113 | 115 | 117 | 118 | 120 | 121 | 125 | 126 | 129 | 131 | 133 | 134 | 136 | 137 | 141 | 142 | 145 | 147 | 149 | 150 | 152 | 153 | 157 | 158 | 161 | 163 | 165 | 166 | 168 | 169 | 173 | 174 | 177 | 179 | 181 | 182 | 184 | 185 | 186 | 187 | 188 | 192 | 194 | 195 | 197 | 198 | 199 | 200 | 204 | 206 | 207 | 208 | 212 | 214 | 216 | 218 | 220 | 222 | 224 | 226 | 228 | 229 | 230 | 231 | 232 | --------------------------------------------------------------------------------