├── .clang-format ├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── CMakeGraphVizOptions.cmake ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── doc └── Wordify.dot.Wordify.png ├── extern ├── CMakeLists.txt ├── arasdk │ └── CMakeLists.txt ├── eventpp │ └── CMakeLists.txt ├── fmt │ └── CMakeLists.txt ├── json │ └── CMakeLists.txt ├── libsamplerate │ └── CMakeLists.txt ├── libsndfile │ └── CMakeLists.txt ├── meta-words │ └── CMakeLists.txt ├── ms-gsl │ └── CMakeLists.txt ├── presonus-plugin-extensions │ └── CMakeLists.txt ├── special-folders │ └── CMakeLists.txt ├── vst3-cpack │ └── CMakeLists.txt ├── vst3sdk │ └── CMakeLists.txt ├── warn-cpp │ └── CMakeLists.txt ├── wave-draw │ └── CMakeLists.txt └── whereami │ └── CMakeLists.txt ├── installer ├── CMakeLists.txt └── linux │ └── install-wordify.sh ├── resource ├── 76FA7B014D2757B49BA55204681B0F2C_snapshot.png ├── 76FA7B014D2757B49BA55204681B0F2C_snapshot_2.0x.png ├── bulb-solid_dark.png ├── bulb-solid_dark_2x.png ├── bulb-solid_dark_3x.png ├── bulb-solid_dark_4x.png ├── bulb-solid_lite.png ├── bulb-solid_lite_2x.png ├── bulb-solid_lite_3x.png ├── bulb-solid_lite_4x.png ├── chevron-down-solid_dark.png ├── chevron-down-solid_dark_2x.png ├── chevron-down-solid_dark_3x.png ├── chevron-down-solid_dark_4x.png ├── chevron-down-solid_lite.png ├── chevron-down-solid_lite_2x.png ├── chevron-down-solid_lite_3x.png ├── chevron-down-solid_lite_4x.png ├── chevron-up-solid_dark.png ├── chevron-up-solid_dark_2x.png ├── chevron-up-solid_dark_3x.png ├── chevron-up-solid_dark_4x.png ├── chevron-up-solid_lite.png ├── chevron-up-solid_lite_2x.png ├── chevron-up-solid_lite_3x.png ├── chevron-up-solid_lite_4x.png ├── editor_res_dark_scheme.uidesc ├── editor_res_signal_dark_scheme.uidesc ├── editor_res_signal_lite_scheme.uidesc ├── magnifying-glass-solid_dark.png ├── magnifying-glass-solid_dark_2x.png ├── magnifying-glass-solid_dark_3x.png ├── magnifying-glass-solid_dark_4x.png ├── magnifying-glass-solid_lite.png ├── magnifying-glass-solid_lite_2x.png ├── magnifying-glass-solid_lite_3x.png ├── magnifying-glass-solid_lite_4x.png ├── moon-solid.png ├── moon-solid_2x.png ├── moon-solid_3x.png ├── moon-solid_4x.png ├── sun-solid.png ├── sun-solid_2x.png ├── sun-solid_3x.png ├── sun-solid_4x.png ├── win32resource.rc └── wordify_editor.uidesc ├── scripts ├── linux │ └── build_release.sh ├── mac │ └── build_release.sh └── win │ └── build_release.bat ├── source ├── ara_document_controller.cpp ├── ara_document_controller.h ├── ara_factory_config.cpp ├── ara_factory_config.h ├── ara_main_factory.h ├── audio_buffer_management.h ├── controllers │ ├── list_controller.cpp │ ├── list_controller.h │ ├── list_entry_controller.cpp │ ├── list_entry_controller.h │ ├── preferences_controller.cpp │ ├── preferences_controller.h │ ├── region_controller.cpp │ ├── region_controller.h │ ├── search_controller.cpp │ ├── search_controller.h │ ├── spinner_controller.cpp │ ├── spinner_controller.h │ ├── waveform_controller.cpp │ └── waveform_controller.h ├── little_helpers.h ├── meta_word_data.cpp ├── meta_word_data.h ├── meta_words_audio_modification.cpp ├── meta_words_audio_modification.h ├── meta_words_audio_source.cpp ├── meta_words_audio_source.h ├── meta_words_editor_renderer.cpp ├── meta_words_editor_renderer.h ├── meta_words_editor_view.cpp ├── meta_words_editor_view.h ├── meta_words_playback_region.cpp ├── meta_words_playback_region.h ├── meta_words_playback_renderer.cpp ├── meta_words_playback_renderer.h ├── meta_words_serde.cpp ├── meta_words_serde.h ├── nonstd.h ├── parameter_ids.h ├── preferences_serde.cpp ├── preferences_serde.h ├── region_data.h ├── region_order_manager.cpp ├── region_order_manager.h ├── search_engine.cpp ├── search_engine.h ├── string_matcher.cpp ├── string_matcher.h ├── task_manager.cpp ├── task_manager.h ├── tiny_selection_model.h ├── version.h ├── views │ ├── hstack_layout.cpp │ ├── hstack_layout.h │ ├── spinner_view.cpp │ ├── spinner_view.h │ ├── view_animations.cpp │ ├── view_animations.h │ ├── waveform_view.cpp │ ├── waveform_view.h │ ├── word_button.cpp │ └── word_button.h ├── whipser_cpp_wrapper.cpp ├── whipser_cpp_wrapper.h ├── wordify_cids.h ├── wordify_defines.h ├── wordify_entry.cpp ├── wordify_single_component.cpp ├── wordify_single_component.h └── wordify_types.h └── test └── test_temp_dir.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | # BasedOnStyle : LVM 2 | # Language : Cpp 3 | AccessModifierOffset : -4 4 | AlignTrailingComments : true 5 | AlignConsecutiveAssignments : true 6 | AlignOperands : true 7 | # AlignConsecutiveDeclarations : true 8 | AllowShortBlocksOnASingleLine : false 9 | AllowShortFunctionsOnASingleLine : Inline 10 | AllowShortIfStatementsOnASingleLine : false 11 | AllowShortLoopsOnASingleLine : false 12 | AlwaysBreakTemplateDeclarations : true 13 | BinPackParameters : false 14 | BreakConstructorInitializersBeforeComma : true 15 | ColumnLimit : 80 16 | ConstructorInitializerAllOnOneLineOrOnePerLine : false 17 | ConstructorInitializerIndentWidth : 0 18 | Cpp11BracedListStyle : true 19 | IndentCaseLabels : true 20 | IndentWidth : 4 21 | PointerBindsToType : true 22 | Standard : Cpp11 23 | TabWidth : 4 24 | UseTab : Never 25 | 26 | # Configure each individual brace in BraceWrapping 27 | BreakBeforeBraces: 'Custom' 28 | 29 | # Control of individual brace wrapping cases 30 | BraceWrapping: { 31 | AfterClass: 'true' 32 | AfterControlStatement: 'true' 33 | AfterEnum : 'true' 34 | AfterFunction : 'true' 35 | AfterNamespace : 'false' 36 | AfterStruct : 'true' 37 | AfterUnion : 'true' 38 | BeforeCatch : 'false' 39 | BeforeElse : 'true' 40 | IndentBraces : 'false' 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake (Linux, macOS, Windows) 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | env: 11 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 12 | BUILD_TYPE: Release 13 | 14 | jobs: 15 | build: 16 | # The CMake configure and build commands are platform agnostic and should work equally 17 | # well on Windows or Mac. You can convert this to a matrix build if you need 18 | # cross-platform coverage. 19 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 20 | runs-on: ${{matrix.os}} 21 | strategy: 22 | matrix: 23 | os: [ubuntu-latest, macos-latest, windows-latest] 24 | # os: [macos-latest, windows-latest] 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - name: Install Dependencies 30 | run: sudo apt-get install libx11-xcb-dev libxcb-util-dev libxcb-cursor-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev libfontconfig1-dev libcairo2-dev libgtkmm-3.0-dev libsqlite3-dev libxcb-keysyms1-dev 31 | if: matrix.os == 'ubuntu-latest' 32 | 33 | - name: Configure CMake 34 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 35 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 36 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 37 | 38 | - name: Build 39 | # Build your program with the given configuration 40 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 41 | 42 | - name: Google Test 43 | working-directory: ${{github.workspace}}/build 44 | shell: bash 45 | run: ctest --verbose -C ${{env.BUILD_TYPE}} 46 | 47 | - name: Upload 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: build-VST3-${{env.BUILD_TYPE}}-${{matrix.os}} 51 | path: build/VST3/${{env.BUILD_TYPE}} 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/launch.json 2 | .vscode 3 | **/.DS_Store 4 | **/*build*/ 5 | -------------------------------------------------------------------------------- /CMakeGraphVizOptions.cmake: -------------------------------------------------------------------------------- 1 | 2 | set(GRAPHVIZ_EXTERNAL_LIBS FALSE) 3 | set(GRAPHVIZ_IGNORE_TARGETS "validator;sdk_hosting;gmock_main;gmock;gtest_main;gtest;VST3Inspector;vstgui_standalone;vstgui_uidescription;meta-words-app;generate;list_formats;make_sine;sfprocess;sndfile-cmp;sndfile-concat;sndfile-to-text;sndfile-salvage;sndfile-play;sndfile-metadata-set;sndfile-metadata-get;sndfile-loopify;sndfile-interleave;sndfile-info;sndfile-convert;sndfile-deinterleave 4 | ;sndfilehandle;vstgui") -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 WordifyOrg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wordify VST 3 Plug-In (ARA) 2 | 3 | [![CMake (Linux, macOS, Windows)](https://github.com/max-and-me/wordify-plugin/actions/workflows/cmake.yml/badge.svg)](https://github.com/max-and-me/wordify-plugin/actions/workflows/cmake.yml) 4 | 5 | Wordify is an ARA / VST 3 plug-in. It transcribes (speech to text) selected clips from the project in a chat-like manner. Clicking on a word, locates to the time position where the word is spoken. An intelligent search makes it possible to quickly find spoken words in clips. 6 | 7 | ![Wordify](https://wordify.org/assets/hero-main/wordify-hero-image.png) 8 | 9 | ## Getting Started 10 | 11 | To clone and create the project, open a command prompt and proceed as follows: 12 | 13 | ### Windows 14 | 15 | ```sh 16 | git clone https://github.com/max-and-me/wordify-plugin.git 17 | mkdir build 18 | cd build 19 | cmake ../wordify-plugin 20 | cmake --build . 21 | ``` 22 | 23 | ### macOS 24 | 25 | ```sh 26 | git clone https://github.com/max-and-me/wordify-plugin.git 27 | mkdir build 28 | cd build 29 | cmake -GXcode ../wordify-plugin 30 | cmake --build . 31 | ``` 32 | 33 | ### Linux 34 | 35 | ```sh 36 | git clone https://github.com/max-and-me/wordify-plugin.git 37 | mkdir build 38 | cd build 39 | cmake -DCMAKE_BUILD_TYPE=Debug ../wordify-plugin 40 | cmake --build . 41 | ``` 42 | 43 | As soon as the project has been successfully built, you will find the plugin bundle in the build folder: ```Debug/VST3/Wordify.vst3``` 44 | 45 | ### Installer 46 | 47 | Build the project in ```Release``` configuration: 48 | 49 | CPack is configured to work with the following Generators: 50 | 51 | * Windows: ```INNOSETUP``` 52 | * macOS: ```productbuild``` 53 | * Linux: ```TGZ``` 54 | 55 | Execute CPack inside the CMake binary directory the ```project```. 56 | 57 | ```sh 58 | cpack -C Release -G . 59 | ``` 60 | 61 | ### Setup whisper's AI model environment 62 | 63 | Wordify needs OpenAI's whisper library and AI models to work. It searches for the models in different locations depending on wether it is build in ```Debug``` or ```Release```. 64 | 65 | Location in ```Debug``` on: 66 | * all platforms ```//build/_deps/meta-words-build/whisper.cpp/models/ggml-base.en.bin``` 67 | 68 | Location in ```Release``` on: 69 | * macOS : ```/Library/Application Support/WordifyOrg/Wordify/ModelData/ggml-medium.bin``` 70 | * Windows: ```C:\ProgramData\WordifyOrg\Wordify\ModelData\ggml-medium.bin``` 71 | * Linux : ```/home//WordifyOrg/Wordify/ModelData/ggml-medium.bin``` 72 | 73 | > In ```Debug``` it uses the small and more inaccurate model ```ggml-base.en.bin``` to speed up debugging as whisper can get quite slow. 74 | 75 | > Run the installer first when you want to build and run the ```Release``` version locally. Otherwise Wordify will not find the model. It needs to be installed first. 76 | 77 | ## Dependency Graph 78 | 79 | ![Alt text](doc/Wordify.dot.Wordify.png "Dependency Graph") 80 | 81 | ```shell 82 | cmake --build . --target Wordify-dependency-graph 83 | cd graphviz 84 | dot -Tpng -O ./Wordify.dot.Wordify 85 | cp ./Wordify.dot.Wordify.png ../../wordify-plugin/doc 86 | ``` 87 | 88 | > TODO: Automate this!! 89 | 90 | ## How to whisper 91 | 92 | How to use the whisper library: 93 | 94 | ```sh 95 | git clone https://github.com/ggerganov/whisper.cpp.git 96 | mkdir build 97 | cd build 98 | cmake -DCMAKE_BUILD_TYPE=Debug ../whisper.cpp 99 | cmake --build . --parallel 100 | ../whisper.cpp/models/download-ggml-model.sh base.en 101 | cd bin 102 | ./main -m ../../whisper.cpp/models/ggml-base.en.bin -f ../../whisper.cpp/samples/jfk.wav -ml 1 103 | ``` 104 | 105 | > See also: https://github.com/ggerganov/whisper.cpp 106 | 107 | ## Getting Help 108 | 109 | * https://vstdev.org 110 | * https://github.com/ggerganov/whisper.cpp -------------------------------------------------------------------------------- /doc/Wordify.dot.Wordify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/doc/Wordify.dot.Wordify.png -------------------------------------------------------------------------------- /extern/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19.0) 2 | 3 | option(BUILD_TESTING "" OFF) 4 | 5 | add_subdirectory(arasdk) 6 | add_subdirectory(eventpp) 7 | add_subdirectory(fmt) 8 | add_subdirectory(json) 9 | add_subdirectory(libsamplerate) 10 | add_subdirectory(libsndfile) 11 | add_subdirectory(meta-words) 12 | add_subdirectory(ms-gsl) 13 | add_subdirectory(presonus-plugin-extensions) 14 | add_subdirectory(vst3sdk) 15 | add_subdirectory(wave-draw) 16 | add_subdirectory(special-folders) 17 | add_subdirectory(vst3-cpack) 18 | add_subdirectory(warn-cpp) 19 | add_subdirectory(whereami) 20 | 21 | 22 | -------------------------------------------------------------------------------- /extern/arasdk/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | arasdk 8 | GIT_REPOSITORY https://github.com/Celemony/ARA_SDK.git 9 | GIT_TAG releases/2.2.0 10 | SOURCE_SUBDIR ARA_Library 11 | ) 12 | 13 | FetchContent_MakeAvailable(arasdk) 14 | -------------------------------------------------------------------------------- /extern/eventpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | eventpp 8 | GIT_REPOSITORY https://github.com/wqking/eventpp.git 9 | GIT_TAG 1224dd6c9bd4577d686ac42334fc545997f5ece1 10 | ) 11 | 12 | FetchContent_MakeAvailable(eventpp) 13 | -------------------------------------------------------------------------------- /extern/fmt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | fmt 8 | GIT_REPOSITORY https://github.com/fmtlib/fmt.git 9 | GIT_TAG 11.2.0 10 | ) 11 | 12 | FetchContent_MakeAvailable(fmt) 13 | -------------------------------------------------------------------------------- /extern/json/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | nlohmann-json 8 | GIT_REPOSITORY "https://github.com/nlohmann/json.git" 9 | GIT_TAG v3.12.0 10 | ) 11 | 12 | FetchContent_MakeAvailable(nlohmann-json) 13 | -------------------------------------------------------------------------------- /extern/libsamplerate/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | libsamplerate 8 | GIT_REPOSITORY https://github.com/libsndfile/libsamplerate.git 9 | GIT_TAG 0.2.2 10 | ) 11 | 12 | option(BUILD_TESTING "" OFF) 13 | 14 | FetchContent_MakeAvailable(libsamplerate) 15 | -------------------------------------------------------------------------------- /extern/libsndfile/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | libsndfile 8 | GIT_REPOSITORY https://github.com/libsndfile/libsndfile.git 9 | GIT_TAG 1.2.2 10 | ) 11 | 12 | option (ENABLE_EXTERNAL_LIBS "Enable FLAC, Vorbis, and Opus codecs" OFF) 13 | option (ENABLE_MPEG "Enable MPEG codecs" OFF) 14 | option (BUILD_PROGRAMS "Build programs" OFF) 15 | option (BUILD_EXAMPLES "Build examples" OFF) 16 | option (ENABLE_CPACK "Enable CPack support" OFF) 17 | set(BUILD_TESTING OFF) 18 | FetchContent_MakeAvailable(libsndfile) 19 | -------------------------------------------------------------------------------- /extern/meta-words/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | meta-words 8 | GIT_REPOSITORY "https://github.com/max-and-me/meta-words.git" 9 | GIT_TAG 3037f71a4799c47edb834a8a88ee89a9d1427ca4 10 | ) 11 | 12 | FetchContent_MakeAvailable(meta-words) 13 | 14 | # Only download if not exist already 15 | set(MAM_WHISPER_CPP_MODEL_FILE_NAME "ggml-medium.bin") 16 | if(EXISTS "${MAM_WHISPER_CPP_MODEL_DOWNLOAD_DIR}/${MAM_WHISPER_CPP_MODEL_FILE_NAME}") 17 | return() 18 | endif() 19 | message(STATUS "[MAM] Downloading model file ${MAM_WHISPER_CPP_MODEL_FILE_NAME}...") 20 | file(DOWNLOAD 21 | https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${MAM_WHISPER_CPP_MODEL_FILE_NAME} 22 | ${MAM_WHISPER_CPP_MODEL_DOWNLOAD_DIR}/${MAM_WHISPER_CPP_MODEL_FILE_NAME} 23 | SHOW_PROGRESS 24 | ) 25 | -------------------------------------------------------------------------------- /extern/ms-gsl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | ms-gsl 8 | GIT_REPOSITORY https://github.com/microsoft/GSL.git 9 | GIT_TAG v4.2.0 10 | ) 11 | 12 | FetchContent_MakeAvailable(ms-gsl) 13 | -------------------------------------------------------------------------------- /extern/presonus-plugin-extensions/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | presonus-plugin-extensions 8 | GIT_REPOSITORY "https://github.com/fenderdigital/presonus-plugin-extensions.git" 9 | GIT_TAG 10-2024 10 | ) 11 | 12 | FetchContent_MakeAvailable(presonus-plugin-extensions) 13 | 14 | add_library(presonus-plugin-extensions INTERFACE) 15 | 16 | target_include_directories(presonus-plugin-extensions 17 | INTERFACE 18 | ${presonus-plugin-extensions_SOURCE_DIR} 19 | ) 20 | -------------------------------------------------------------------------------- /extern/special-folders/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | special-folders 8 | GIT_REPOSITORY "https://github.com/hansen-audio/special-folders.git" 9 | GIT_TAG b209cffd230a0d2c807837c4192f1cc72a943c58 10 | ) 11 | 12 | FetchContent_MakeAvailable(special-folders) 13 | -------------------------------------------------------------------------------- /extern/vst3-cpack/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14.0) 2 | 3 | include(FetchContent) 4 | 5 | FetchContent_Declare( 6 | vst3-cpack 7 | GIT_REPOSITORY "https://github.com/hansen-audio/vst3-cpack.git" 8 | GIT_TAG main 9 | ) 10 | 11 | FetchContent_MakeAvailable(vst3-cpack) 12 | -------------------------------------------------------------------------------- /extern/vst3sdk/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.14.0) 3 | 4 | include(FetchContent) 5 | 6 | option(VSTGUI_STANDALONE "VSTGUI Standalone library" OFF) 7 | option(VSTGUI_STANDALONE_EXAMPLES "VSTGUI Standalone examples" OFF) 8 | option(VSTGUI_TOOLS "Build VSTGUI Tools" OFF) 9 | #set(SMTG_ENABLE_VSTGUI_SUPPORT OFF) 10 | 11 | # FetchContent_Declare( 12 | # vstgui 13 | # GIT_REPOSITORY https://github.com/steinbergmedia/vstgui.git 14 | # GIT_TAG fe9370d5b7136d763a90987e8f97cca1964d1e4b 15 | # ) 16 | # 17 | # FetchContent_GetProperties(vstgui) 18 | # if(NOT vstgui_POPULATED) 19 | # FetchContent_Populate(vstgui) 20 | # endif() 21 | # 22 | # option(SMTG_ENABLE_VST3_PLUGIN_EXAMPLES "Enable VST 3 Plug-in Examples" OFF) 23 | # option(SMTG_ENABLE_VST3_HOSTING_EXAMPLES "Enable VST 3 Hosting Examples" OFF) 24 | # option(SMTG_CREATE_PLUGIN_LINK $,ON,OFF>) 25 | # if(WIN32) 26 | # option(SMTG_CREATE_PLUGIN_LINK OFF) 27 | # endif() 28 | 29 | # Disable symbolic link creation on all platforms because of a bug in v3.7.14_build_55 30 | option(SMTG_CREATE_PLUGIN_LINK OFF) 31 | 32 | FetchContent_Declare( 33 | vst3sdk 34 | GIT_REPOSITORY https://github.com/steinbergmedia/vst3sdk.git 35 | GIT_TAG v3.7.14_build_55 36 | ) 37 | 38 | #set(VSTGUI_SOURCE_DIR "${vstgui_SOURCE_DIR}") 39 | FetchContent_MakeAvailable(vst3sdk) 40 | 41 | set(public_sdk_SOURCE_DIR "${vst3sdk_SOURCE_DIR}/public.sdk") 42 | # smtg_enable_vstgui_support(VSTGUI_SOURCE_DIR ${vstgui_SOURCE_DIR}) 43 | -------------------------------------------------------------------------------- /extern/warn-cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14.0) 2 | 3 | include(FetchContent) 4 | 5 | FetchContent_Declare( 6 | warn-cpp 7 | GIT_REPOSITORY "https://github.com/max-and-me/warn-cpp.git" 8 | GIT_TAG main 9 | ) 10 | 11 | FetchContent_MakeAvailable(warn-cpp) 12 | -------------------------------------------------------------------------------- /extern/wave-draw/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14.0) 2 | 3 | include(FetchContent) 4 | 5 | FetchContent_Declare( 6 | wave-draw 7 | GIT_REPOSITORY "https://github.com/max-and-me/wave-draw.git" 8 | GIT_TAG main 9 | ) 10 | 11 | FetchContent_MakeAvailable(wave-draw) 12 | -------------------------------------------------------------------------------- /extern/whereami/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14.0) 2 | 3 | include(FetchContent) 4 | 5 | FetchContent_Declare( 6 | whereami 7 | GIT_REPOSITORY "https://github.com/gpakosz/whereami.git" 8 | GIT_TAG dcb52a058dc14530ba9ae05e4339bd3ddfae0e0e 9 | ) 10 | 11 | FetchContent_MakeAvailable(whereami) 12 | 13 | add_library(whereami 14 | ${whereami_SOURCE_DIR}/src/whereami.c 15 | ${whereami_SOURCE_DIR}/src/whereami.h 16 | ) 17 | 18 | target_include_directories(whereami 19 | PUBLIC 20 | ${whereami_SOURCE_DIR}/src 21 | ) -------------------------------------------------------------------------------- /installer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20.0) 2 | 3 | set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE.txt) 4 | 5 | message(STATUS "[MAM] CPACK_RESOURCE_FILE_LICENSE: ${CPACK_RESOURCE_FILE_LICENSE}") 6 | 7 | # Add Wordify plugin 8 | get_target_property(PLUGIN_PACKAGE_PATH Wordify SMTG_PLUGIN_PACKAGE_PATH) 9 | vst3_cpack_configure( 10 | PLUGIN_PACKAGE_PATH "${PLUGIN_PACKAGE_PATH}" 11 | ) 12 | 13 | # Add another install component for the model data 14 | cpack_add_component( 15 | modeldata 16 | DISPLAY_NAME "Model Data" 17 | INSTALL_TYPES full 18 | REQUIRED 19 | ) 20 | 21 | # Installation path for the model data: 22 | # Windows: C:\Program Data\WordifyOrg\Wordify\ModelData\... 23 | # macOS : /Library/Application Support/WordifyOrg/Wordify/ModelData/... 24 | set(CPACK_INNOSETUP_modeldata_INSTALL_DIRECTORY "{commonappdata}") # C:\Program Data\ 25 | set(MAM_MODEL_DATA_FOLDER "${CPACK_PACKAGE_VENDOR}/${PROJECT_NAME}/${MAM_GGML_DIRECTORY_NAME}") 26 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 27 | # On macos we need to add 'Library/Application Support' 28 | set(MAM_MODEL_DATA_FOLDER "Library/Application Support/${MAM_MODEL_DATA_FOLDER}") 29 | endif() 30 | 31 | install( 32 | FILES "${MAM_WORDIFY_MODEL_FILE}" 33 | DESTINATION "${MAM_MODEL_DATA_FOLDER}" 34 | COMPONENT modeldata 35 | ) 36 | 37 | list(APPEND CPACK_COMPONENTS_ALL modeldata) 38 | 39 | message(STATUS "[MAM] MAM_MODEL_DATA_FOLDER: ${MAM_MODEL_DATA_FOLDER}") 40 | 41 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 42 | install( 43 | FILES 44 | "${CPACK_RESOURCE_FILE_LICENSE}" 45 | DESTINATION . 46 | COMPONENT vst3plugin 47 | PERMISSIONS 48 | OWNER_WRITE OWNER_READ 49 | GROUP_READ 50 | WORLD_READ 51 | ) 52 | install( 53 | FILES 54 | "linux/install-wordify.sh" 55 | DESTINATION . 56 | COMPONENT vst3plugin 57 | PERMISSIONS 58 | OWNER_EXECUTE OWNER_WRITE OWNER_READ 59 | GROUP_READ GROUP_EXECUTE 60 | WORLD_READ WORLD_EXECUTE 61 | ) 62 | endif() 63 | 64 | include(CPack) -------------------------------------------------------------------------------- /installer/linux/install-wordify.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Change directory to the script directory 4 | cd "$(dirname "$0")" 5 | 6 | # Define some vars here 7 | COMPANY_NAME="WordifyOrg" 8 | PRODUCT_NAME="Wordify" 9 | LICENSE_FILE="LICENSE.txt" 10 | SRCPATH=$(pwd) 11 | 12 | # From the VST3 documentation 13 | VST3DIR="$HOME/.vst3" 14 | DATADIR="$HOME/$COMPANY_NAME" 15 | mkdir -p ${VST3DIR} 16 | 17 | # Help will be show when -h|--help 18 | print_help() 19 | { 20 | echo "USAGE:" 21 | echo " ./install [OPTIONS]" 22 | echo 23 | echo "FLAGS:" 24 | echo " -h, --help Prints help information" 25 | echo 26 | echo "OPTIONS:" 27 | echo " -l, --license Show license information and exit." 28 | echo " -u, --uninstall Call the uninstaller." 29 | exit 30 | } 31 | 32 | # Simply prints the license.txt into the terminal. 33 | print_license() 34 | { 35 | FILE=${SRCPATH}/${LICENSE_FILE} 36 | cat "${FILE}" 37 | exit 38 | } 39 | 40 | # Copies files into target directories. 41 | install() 42 | { 43 | echo "Installing..." 44 | echo 45 | SOURCE="${SRCPATH}/${PRODUCT_NAME}.vst3" 46 | TARGET="${VST3DIR}" 47 | echo " Copy ${PRODUCT_NAME}.vst3 to ${TARGET} ... " 48 | if cp -R ${SOURCE} ${TARGET} 49 | then echo " ...done!" 50 | else echo " ...failed!" 51 | fi 52 | 53 | SOURCE="${SRCPATH}/${COMPANY_NAME}" 54 | TARGET="${DATADIR}" 55 | echo " Copy ${PRODUCT_NAME} data to ${DATADIR} ... " 56 | if mkdir -p "${TARGET}" && cp -R "${SOURCE}/." "$_" # $_ is return of mkdir 57 | then echo " ...done!" 58 | else echo " ...failed!" 59 | fi 60 | 61 | # Only install if apt-get is available e.g. Ubuntu. On Manjaro, no need to install anything. 62 | if [ -x "$(command -v apk-get)" ]; 63 | then 64 | # $ Use 'ldd Wordify.so' to list all dependencies. 65 | echo " Install system packages ... " 66 | sudo apt-get install libfreetype6 libx11-xcb1 \ 67 | libfreetype6 \ 68 | libxcb1 \ 69 | libxcb-util1 \ 70 | libxcb-cursor0 \ 71 | libxcb-xkb1 \ 72 | libxkbcommon0 \ 73 | libxkbcommon-x11-0 \ 74 | libcairo2 \ 75 | libfontconfig1 \ 76 | libexpat1 \ 77 | libstdc++6 \ 78 | libsqlite3-0 \ 79 | libxcb-keysyms1 80 | echo " ...done!" 81 | fi 82 | 83 | exit 84 | } 85 | 86 | # Deletes folders from where they have been installed by this installer. 87 | uninstall() 88 | { 89 | DIR="${VST3DIR}/${PRODUCT_NAME}.vst3" 90 | rm -rf ${DIR} 91 | exit 92 | } 93 | 94 | # Parse script options. 95 | while [ $# -gt 0 ] ; 96 | do 97 | case "$1" in 98 | -h|--help) 99 | print_help 100 | ;; 101 | -l|--license) 102 | print_license 103 | ;; 104 | -u|--uninstall) 105 | uninstall 106 | ;; 107 | *) echo "Invalid options passed! Use --help to show valid options." ; exit 1 ;; 108 | esac 109 | done 110 | 111 | # Will be called if no options have been passed. 112 | install -------------------------------------------------------------------------------- /resource/76FA7B014D2757B49BA55204681B0F2C_snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/76FA7B014D2757B49BA55204681B0F2C_snapshot.png -------------------------------------------------------------------------------- /resource/76FA7B014D2757B49BA55204681B0F2C_snapshot_2.0x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/76FA7B014D2757B49BA55204681B0F2C_snapshot_2.0x.png -------------------------------------------------------------------------------- /resource/bulb-solid_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_dark.png -------------------------------------------------------------------------------- /resource/bulb-solid_dark_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_dark_2x.png -------------------------------------------------------------------------------- /resource/bulb-solid_dark_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_dark_3x.png -------------------------------------------------------------------------------- /resource/bulb-solid_dark_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_dark_4x.png -------------------------------------------------------------------------------- /resource/bulb-solid_lite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_lite.png -------------------------------------------------------------------------------- /resource/bulb-solid_lite_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_lite_2x.png -------------------------------------------------------------------------------- /resource/bulb-solid_lite_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_lite_3x.png -------------------------------------------------------------------------------- /resource/bulb-solid_lite_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/bulb-solid_lite_4x.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_dark.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_dark_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_dark_2x.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_dark_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_dark_3x.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_dark_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_dark_4x.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_lite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_lite.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_lite_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_lite_2x.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_lite_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_lite_3x.png -------------------------------------------------------------------------------- /resource/chevron-down-solid_lite_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-down-solid_lite_4x.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_dark.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_dark_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_dark_2x.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_dark_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_dark_3x.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_dark_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_dark_4x.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_lite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_lite.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_lite_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_lite_2x.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_lite_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_lite_3x.png -------------------------------------------------------------------------------- /resource/chevron-up-solid_lite_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/chevron-up-solid_lite_4x.png -------------------------------------------------------------------------------- /resource/editor_res_dark_scheme.uidesc: -------------------------------------------------------------------------------- 1 | { 2 | "vstgui-ui-description": { 3 | "version": "1", 4 | "bitmaps": {}, 5 | "fonts": { 6 | "region_start_time_font": { 7 | "font-name": "Arial", 8 | "size": "14" 9 | }, 10 | "region_duration_time_font": { 11 | "font-name": "Arial", 12 | "size": "12" 13 | }, 14 | "region_title_font": { 15 | "bold": "true", 16 | "font-name": "Arial", 17 | "size": "14" 18 | }, 19 | "region_transcript_font": { 20 | "font-name": "Arial", 21 | "size": "15" 22 | } 23 | }, 24 | "colors": { 25 | "main_background": "#25282bff", 26 | "region_start_time_color": "#818181ff", 27 | "region_transcript_button_color": "#afb5bbff" 28 | }, 29 | "gradients": { 30 | "Default TextButton Gradient": [ 31 | { 32 | "rgba": "#dcdcdcff", 33 | "start": "0" 34 | }, 35 | { 36 | "rgba": "#b4b4b4ff", 37 | "start": "1" 38 | } 39 | ], 40 | "Default TextButton Gradient Highlighted": [ 41 | { 42 | "rgba": "#b4b4b4ff", 43 | "start": "0" 44 | }, 45 | { 46 | "rgba": "#646464ff", 47 | "start": "1" 48 | } 49 | ], 50 | "play_head_color": [ 51 | { 52 | "rgba": "#FFFFFFFF", 53 | "start": "0" 54 | }, 55 | { 56 | "rgba": "#FFFFFFFF", 57 | "start": "1" 58 | } 59 | ], 60 | "transparent_gradient": [ 61 | { 62 | "rgba": "#00000000", 63 | "start": "0" 64 | }, 65 | { 66 | "rgba": "#00000000", 67 | "start": "1" 68 | } 69 | ], 70 | "meta_word_text_gradient": [ 71 | { 72 | "rgba": "#afb5bbff", 73 | "start": "0" 74 | }, 75 | { 76 | "rgba": "#afb5bbff", 77 | "start": "1" 78 | } 79 | ], 80 | "waveform_background": [ 81 | { 82 | "rgba": "#cccccc7f", 83 | "start": "0" 84 | }, 85 | { 86 | "rgba": "#9b9b9b7f", 87 | "start": "1" 88 | } 89 | ] 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /resource/editor_res_signal_dark_scheme.uidesc: -------------------------------------------------------------------------------- 1 | { 2 | "vstgui-ui-description": { 3 | "version": "1", 4 | "bitmaps": { 5 | "sun-solid": { 6 | "path": "sun-solid.png" 7 | }, 8 | "moon-solid": { 9 | "path": "moon-solid.png" 10 | }, 11 | "sun-solid_2x": { 12 | "path": "sun-solid_2x.png" 13 | }, 14 | "moon-solid_2x": { 15 | "path": "moon-solid_2x.png" 16 | }, 17 | "sun-solid_3x": { 18 | "path": "sun-solid_3x.png" 19 | }, 20 | "moon-solid_3x": { 21 | "path": "moon-solid_3x.png" 22 | }, 23 | "sun-solid_4x": { 24 | "path": "sun-solid_4x.png" 25 | }, 26 | "moon-solid_4x": { 27 | "path": "moon-solid_4x.png" 28 | }, 29 | "magnifying-glass": { 30 | "path": "magnifying-glass-solid_dark.png" 31 | }, 32 | "magnifying-glass_2x": { 33 | "path": "magnifying-glass-solid_dark_2x.png" 34 | }, 35 | "magnifying-glass_3x": { 36 | "path": "magnifying-glass-solid_dark_3x.png" 37 | }, 38 | "magnifying-glass_4x": { 39 | "path": "magnifying-glass-solid_dark_4x.png" 40 | }, 41 | "bulb": { 42 | "path": "bulb-solid_dark.png" 43 | }, 44 | "bulb_2x": { 45 | "path": "bulb-solid_dark_2x.png" 46 | }, 47 | "bulb_3x": { 48 | "path": "bulb-solid_dark_3x.png" 49 | }, 50 | "bulb_4x": { 51 | "path": "bulb-solid_dark_4x.png" 52 | }, 53 | "chevron-up": { 54 | "path": "chevron-up-solid_dark.png" 55 | }, 56 | "chevron-up_2x": { 57 | "path": "chevron-up-solid_dark_2x.png" 58 | }, 59 | "chevron-up_3x": { 60 | "path": "chevron-up-solid_dark_3x.png" 61 | }, 62 | "chevron-up_4x": { 63 | "path": "chevron-up-solid_dark_4x.png" 64 | }, 65 | "chevron-down": { 66 | "path": "chevron-down-solid_dark.png" 67 | }, 68 | "chevron-down_2x": { 69 | "path": "chevron-down-solid_dark_2x.png" 70 | }, 71 | "chevron-down_3x": { 72 | "path": "chevron-down-solid_dark_3x.png" 73 | }, 74 | "chevron-down_4x": { 75 | "path": "chevron-down-solid_dark_4x.png" 76 | } 77 | }, 78 | "fonts": { 79 | "region_start_time_font": { 80 | "font-name": "Arial", 81 | "size": "14" 82 | }, 83 | "region_duration_time_font": { 84 | "font-name": "Arial", 85 | "size": "12" 86 | }, 87 | "region_title_font": { 88 | "bold": "true", 89 | "font-name": "Arial", 90 | "size": "14" 91 | }, 92 | "region_transcript_font": { 93 | "font-name": "Arial", 94 | "size": "15" 95 | }, 96 | "search_field_font": { 97 | "bold": "true", 98 | "font-name": "Arial", 99 | "size": "16" 100 | }, 101 | "spinner_badge_font": { 102 | "font-name": "Arial", 103 | "size": "12" 104 | } 105 | }, 106 | "colors": { 107 | "header_background_color": "#282a31ff", 108 | "body_background_color": "#1b1c20ff", 109 | "footer_background_color": "#1a1c20ff", 110 | "kebab_menu_color": "#e2e1e5ff", 111 | "loading_indicator_background_color": "#303133ff", 112 | "loading_indicator_dot_color": "#e2e1e5ff", 113 | "region_start_time_color": "#bebfc5ff", 114 | "region_duration_time_color": "#8d8d94ff", 115 | "scroll_bar_handle_color": "#919193ff", 116 | "search_field_background_color": "#282a31ff", 117 | "search_field_text_color": "#d4d4d4ff", 118 | "spinner_badge_background_color": "#2c6bedff", 119 | "spinner_badge_text_color": "#e2e1e6ff", 120 | "transcript_text_color": "#e2e1e6ff", 121 | "transcript_text_hilite_color": "#e2e1e680", 122 | "search_hilite_bgr_color": "#C8C8C880", 123 | "search_hilite_text_color": "#FFFFFF", 124 | "search_select_hilite_bgr_color": "#FFFF00", 125 | "search_select_hilite_text_color": "#000000" 126 | }, 127 | "gradients": { 128 | "Default TextButton Gradient": [ 129 | { 130 | "rgba": "#dcdcdcff", 131 | "start": "0" 132 | }, 133 | { 134 | "rgba": "#b4b4b4ff", 135 | "start": "1" 136 | } 137 | ], 138 | "Default TextButton Gradient Highlighted": [ 139 | { 140 | "rgba": "#b4b4b4ff", 141 | "start": "0" 142 | }, 143 | { 144 | "rgba": "#646464ff", 145 | "start": "1" 146 | } 147 | ], 148 | "play_head_color": [ 149 | { 150 | "rgba": "#FFFFFFFF", 151 | "start": "0" 152 | }, 153 | { 154 | "rgba": "#FFFFFFFF", 155 | "start": "1" 156 | } 157 | ], 158 | "transparent_gradient": [ 159 | { 160 | "rgba": "#00000000", 161 | "start": "0" 162 | }, 163 | { 164 | "rgba": "#00000000", 165 | "start": "1" 166 | } 167 | ], 168 | "waveform_background_gradient": [ 169 | { 170 | "rgba": "#2f3133ff", 171 | "start": "0" 172 | }, 173 | { 174 | "rgba": "#2f3133ff", 175 | "start": "1" 176 | } 177 | ], 178 | "search_next_previous_button_highlighted_background_gradient": [ 179 | { 180 | "rgba": "#393b41ff", 181 | "start": "0" 182 | }, 183 | { 184 | "rgba": "#393b41ff", 185 | "start": "1" 186 | } 187 | ] 188 | } 189 | } 190 | } -------------------------------------------------------------------------------- /resource/editor_res_signal_lite_scheme.uidesc: -------------------------------------------------------------------------------- 1 | { 2 | "vstgui-ui-description": { 3 | "version": "1", 4 | "bitmaps": { 5 | "sun-solid": { 6 | "path": "sun-solid.png" 7 | }, 8 | "moon-solid": { 9 | "path": "moon-solid.png" 10 | }, 11 | "sun-solid_2x": { 12 | "path": "sun-solid_2x.png" 13 | }, 14 | "moon-solid_2x": { 15 | "path": "moon-solid_2x.png" 16 | }, 17 | "sun-solid_3x": { 18 | "path": "sun-solid_3x.png" 19 | }, 20 | "moon-solid_3x": { 21 | "path": "moon-solid_3x.png" 22 | }, 23 | "sun-solid_4x": { 24 | "path": "sun-solid_4x.png" 25 | }, 26 | "moon-solid_4x": { 27 | "path": "moon-solid_4x.png" 28 | }, 29 | "magnifying-glass": { 30 | "path": "magnifying-glass-solid_lite.png" 31 | }, 32 | "magnifying-glass_2x": { 33 | "path": "magnifying-glass-solid_lite_2x.png" 34 | }, 35 | "magnifying-glass_3x": { 36 | "path": "magnifying-glass-solid_lite_3x.png" 37 | }, 38 | "magnifying-glass_4x": { 39 | "path": "magnifying-glass-solid_lite_4x.png" 40 | }, 41 | "bulb": { 42 | "path": "bulb-solid_lite.png" 43 | }, 44 | "bulb_2x": { 45 | "path": "bulb-solid_lite_2x.png" 46 | }, 47 | "bulb_3x": { 48 | "path": "bulb-solid_lite_3x.png" 49 | }, 50 | "bulb_4x": { 51 | "path": "bulb-solid_lite_4x.png" 52 | }, 53 | "chevron-up": { 54 | "path": "chevron-up-solid_lite.png" 55 | }, 56 | "chevron-up_2x": { 57 | "path": "chevron-up-solid_lite_2x.png" 58 | }, 59 | "chevron-up_3x": { 60 | "path": "chevron-up-solid_lite_3x.png" 61 | }, 62 | "chevron-up_4x": { 63 | "path": "chevron-up-solid_lite_4x.png" 64 | }, 65 | "chevron-down": { 66 | "path": "chevron-down-solid_lite.png" 67 | }, 68 | "chevron-down_2x": { 69 | "path": "chevron-down-solid_lite_2x.png" 70 | }, 71 | "chevron-down_3x": { 72 | "path": "chevron-down-solid_lite_3x.png" 73 | }, 74 | "chevron-down_4x": { 75 | "path": "chevron-down-solid_lite_4x.png" 76 | } 77 | }, 78 | "fonts": { 79 | "region_start_time_font": { 80 | "font-name": "Arial", 81 | "size": "14" 82 | }, 83 | "region_duration_time_font": { 84 | "font-name": "Arial", 85 | "size": "12" 86 | }, 87 | "region_title_font": { 88 | "bold": "true", 89 | "font-name": "Arial", 90 | "size": "14" 91 | }, 92 | "region_transcript_font": { 93 | "font-name": "Arial", 94 | "size": "15" 95 | }, 96 | "search_field_font": { 97 | "bold": "true", 98 | "font-name": "Arial", 99 | "size": "16" 100 | }, 101 | "spinner_badge_font": { 102 | "font-name": "Arial", 103 | "size": "12" 104 | } 105 | }, 106 | "colors": { 107 | "header_background_color": "#edf0f6ff", 108 | "body_background_color": "#fafcffff", 109 | "footer_background_color": "#fafcffff", 110 | "kebab_menu_color": "#1b1b1dff", 111 | "loading_indicator_background_color": "#e8ebf2ff", 112 | "loading_indicator_dot_color": "#545863ff", 113 | "region_start_time_color": "#545863ff", 114 | "region_duration_time_color": "#545863ff", 115 | "scroll_bar_handle_color": "#878889ff", 116 | "search_field_background_color": "#edf0f6ff", 117 | "search_field_text_color": "#1c1a1dff", 118 | "spinner_badge_background_color": "#2c6bedff", 119 | "spinner_badge_text_color": "#e2e1e6ff", 120 | "transcript_text_color": "#1c1a1dff", 121 | "transcript_text_hilite_color": "#1c1a1d80", 122 | "search_hilite_bgr_color": "#C8C8C8", 123 | "search_hilite_text_color": "#000000", 124 | "search_select_hilite_bgr_color": "#FFFF00", 125 | "search_select_hilite_text_color": "#000000" 126 | }, 127 | "gradients": { 128 | "Default TextButton Gradient": [ 129 | { 130 | "rgba": "#dcdcdcff", 131 | "start": "0" 132 | }, 133 | { 134 | "rgba": "#b4b4b4ff", 135 | "start": "1" 136 | } 137 | ], 138 | "Default TextButton Gradient Highlighted": [ 139 | { 140 | "rgba": "#b4b4b4ff", 141 | "start": "0" 142 | }, 143 | { 144 | "rgba": "#646464ff", 145 | "start": "1" 146 | } 147 | ], 148 | "play_head_color": [ 149 | { 150 | "rgba": "#FFFFFFFF", 151 | "start": "0" 152 | }, 153 | { 154 | "rgba": "#FFFFFFFF", 155 | "start": "1" 156 | } 157 | ], 158 | "transparent_gradient": [ 159 | { 160 | "rgba": "#00000000", 161 | "start": "0" 162 | }, 163 | { 164 | "rgba": "#00000000", 165 | "start": "1" 166 | } 167 | ], 168 | "waveform_background_gradient": [ 169 | { 170 | "rgba": "#e7ebf3ff", 171 | "start": "0" 172 | }, 173 | { 174 | "rgba": "#e7ebf3ff", 175 | "start": "1" 176 | } 177 | ], 178 | "search_next_previous_button_highlighted_background_gradient": [ 179 | { 180 | "rgba": "#dcdee4ff", 181 | "start": "0" 182 | }, 183 | { 184 | "rgba": "#dcdee4ff", 185 | "start": "1" 186 | } 187 | ] 188 | } 189 | } 190 | } -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_dark.png -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_dark_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_dark_2x.png -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_dark_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_dark_3x.png -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_dark_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_dark_4x.png -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_lite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_lite.png -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_lite_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_lite_2x.png -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_lite_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_lite_3x.png -------------------------------------------------------------------------------- /resource/magnifying-glass-solid_lite_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/magnifying-glass-solid_lite_4x.png -------------------------------------------------------------------------------- /resource/moon-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/moon-solid.png -------------------------------------------------------------------------------- /resource/moon-solid_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/moon-solid_2x.png -------------------------------------------------------------------------------- /resource/moon-solid_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/moon-solid_3x.png -------------------------------------------------------------------------------- /resource/moon-solid_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/moon-solid_4x.png -------------------------------------------------------------------------------- /resource/sun-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/sun-solid.png -------------------------------------------------------------------------------- /resource/sun-solid_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/sun-solid_2x.png -------------------------------------------------------------------------------- /resource/sun-solid_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/sun-solid_3x.png -------------------------------------------------------------------------------- /resource/sun-solid_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/resource/sun-solid_4x.png -------------------------------------------------------------------------------- /resource/win32resource.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../source/version.h" 3 | 4 | #define APSTUDIO_READONLY_SYMBOLS 5 | 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // Version 8 | ///////////////////////////////////////////////////////////////////////////// 9 | VS_VERSION_INFO VERSIONINFO 10 | FILEVERSION MAJOR_VERSION_INT, SUB_VERSION_INT 11 | PRODUCTVERSION MAJOR_VERSION_INT, SUB_VERSION_INT 12 | FILEFLAGSMASK 0x3fL 13 | #ifdef _DEBUG 14 | FILEFLAGS 0x1L 15 | #else 16 | FILEFLAGS 0x0L 17 | #endif 18 | FILEOS 0x40004L 19 | FILETYPE 0x1L 20 | FILESUBTYPE 0x0L 21 | BEGIN 22 | BLOCK "StringFileInfo" 23 | BEGIN 24 | BLOCK "040004e4" 25 | BEGIN 26 | VALUE "FileVersion", FULL_VERSION_STR"\0" 27 | VALUE "ProductVersion", FULL_VERSION_STR"\0" 28 | VALUE "OriginalFilename", stringOriginalFilename"\0" 29 | VALUE "FileDescription", stringFileDescription"\0" 30 | VALUE "InternalName", stringFileDescription"\0" 31 | VALUE "ProductName", stringFileDescription"\0" 32 | VALUE "CompanyName", stringCompanyName"\0" 33 | VALUE "LegalCopyright", stringLegalCopyright"\0" 34 | VALUE "LegalTrademarks", stringLegalTrademarks"\0" 35 | //VALUE "PrivateBuild", " \0" 36 | //VALUE "SpecialBuild", " \0" 37 | //VALUE "Comments", " \0" 38 | END 39 | END 40 | BLOCK "VarFileInfo" 41 | BEGIN 42 | VALUE "Translation", 0x400, 1252 43 | END 44 | END 45 | -------------------------------------------------------------------------------- /scripts/linux/build_release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | git clone https://github.com/max-and-me/wordify-plugin.git 4 | mkdir build 5 | cd build 6 | cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../wordify-plugin 7 | cmake --build . --target Wordify --config Release 8 | cpack -C Release -G TGZ . -------------------------------------------------------------------------------- /scripts/mac/build_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Exit immediately if any command fails 4 | set -e 5 | 6 | # Display help function 7 | function display_help { 8 | echo "Usage: $0 [option] arg1 arg2 arg3" 9 | echo 10 | echo "This script clones the repository, creates and builds the project, signs the binary, creates and signs the installer and runs Apples notary services." 11 | echo 12 | echo "Options:" 13 | echo " -h, --help Show this help message" 14 | echo 15 | echo "Arguments:" 16 | echo " arg1 The first argument, Developer ID Application as string" 17 | echo " arg2 The second argument, Developer ID Installer as string" 18 | echo " arg3 The third argument, Git tag as string (Optional)" 19 | echo 20 | echo "Example:" 21 | echo " $0 'Developer ID Application: My Company (E53ZE5AABB)' 'Developer ID Installer: My Company (E53ZE5AABB)' v2025.02" 22 | echo 23 | exit 0 24 | } 25 | 26 | # Check if -h or --help is passed 27 | if [[ "$1" == "-h" || "$1" == "--help" ]]; then 28 | display_help 29 | fi 30 | 31 | # If no arguments are provided, show error and help 32 | if [[ $# -le 2 ]]; then 33 | echo "[MAM] MacOS_Build_Release_sh: Error, insufficient arguments provided." 34 | display_help 35 | fi 36 | 37 | # Clone project 38 | echo "[MAM] MacOS_Build_Release_sh: Clone Project" 39 | git clone https://github.com/max-and-me/wordify-plugin.git 40 | 41 | echo "[MAM] MacOS_Build_Release_sh: Checkout Project with tag" 42 | if [ -n "$3" ]; then 43 | cd wordify-plugin 44 | git checkout -b checkout-at-"$3" "$3" 45 | cd .. 46 | fi 47 | 48 | # Cmake project 49 | mkdir build 50 | cd build 51 | echo "[MAM] MacOS_Build_Release_sh: CMake project" 52 | cmake -GXcode ../wordify-plugin 53 | cmake --build . --target Wordify --config Release 54 | 55 | # Sign the binaries 56 | echo "[MAM] MacOS_Build_Release_sh: Sign the binaries" 57 | codesign --sign "$1" -f -o runtime --timestamp -v ./VST3/Release/Wordify.vst3/Contents/MacOS/whisper-cli 58 | codesign --sign "$1" -f -o runtime --timestamp -v ./VST3/Release/Wordify.vst3/Contents/MacOS/Wordify 59 | codesign --sign "$1" -f -o runtime --timestamp -v ./VST3/Release/Wordify.vst3 60 | 61 | # Check binary signing 62 | echo "[MAM] MacOS_Build_Release_sh: Check binary signing:" 63 | codesign --display -vv ./VST3/Release/Wordify.vst3 64 | 65 | # Run the packager 66 | echo "MacOS_Build_Release_sh: Run the packager:" 67 | cpack -C Release -G productbuild . 68 | 69 | # Find the generated installer 70 | installer_name=$(ls *.pkg) 71 | 72 | # Check if a .pkg file exists 73 | if [ -z "$installer_name" ]; then 74 | echo "No .pkg file found" 75 | exit 1 76 | fi 77 | 78 | # Handle multiple .pkg files 79 | if [ $(echo "$installer_name" | wc -l) -ne 1 ]; then 80 | echo "Multiple .pkg files found:" 81 | echo "$installer_name" 82 | echo "Please make sure only one package exists." 83 | exit 1 84 | fi 85 | 86 | # Remove extension 87 | installer_name_no_ext=${installer_name%.pkg} 88 | installer_name_signed="${installer_name_no_ext}-signed.pkg" 89 | 90 | # Sign the Installer 91 | echo "[MAM] MacOS_Build_Release_sh: Sign the Installer:" 92 | productsign --sign "$2" "./${installer_name}" "./${installer_name_signed}" 93 | 94 | # Check installer signing 95 | echo "[MAM] MacOS_Build_Release_sh: Check installer signing:" 96 | pkgutil --check-signature "./${installer_name_signed}" 97 | 98 | ## Run the notary service 99 | echo "[MAM] MacOS_Build_Release_sh: Run the notary service:" 100 | xcrun notarytool submit "./${installer_name_signed}" --keychain-profile NOTARYTOOL_PASSWORD --wait 101 | 102 | ## Run Stapler 103 | echo "[MAM] MacOS_Build_Release_sh: Staple the result:" 104 | xcrun stapler staple "./${installer_name_signed}" 105 | -------------------------------------------------------------------------------- /scripts/win/build_release.bat: -------------------------------------------------------------------------------- 1 | git clone https://github.com/max-and-me/wordify-plugin.git 2 | mkdir build 3 | cd build 4 | cmake ..\wordify-plugin 5 | cmake --build . --config Release 6 | cpack -C Release -G INNOSETUP . -------------------------------------------------------------------------------- /source/ara_factory_config.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "ara_factory_config.h" 4 | #include "projectversion.h" 5 | #include "wordify_defines.h" 6 | 7 | #define WORDIFY_FACTORY_ID "org.wordify.plugin.arafactory" 8 | #define WORDIFY_DOCUMENT_ARCHIVE_ID \ 9 | "org.wordify.plugin.aradocumentarchive.version1" 10 | #define WORDIFY_FILECHUNK_ARCHIVE_ID \ 11 | "org.wordify.plugin.arafilechunkarchive.version1" 12 | 13 | namespace mam { 14 | 15 | //------------------------------------------------------------------------ 16 | // ARAFactoryConfig 17 | //------------------------------------------------------------------------ 18 | const char* ARAFactoryConfig::getFactoryID() const noexcept 19 | { 20 | return WORDIFY_FACTORY_ID; 21 | } 22 | 23 | //-------------------------------------------------------------------- 24 | const char* ARAFactoryConfig::getPlugInName() const noexcept 25 | { 26 | return PLUGIN_NAME_STR; 27 | } 28 | 29 | //-------------------------------------------------------------------- 30 | const char* ARAFactoryConfig::getManufacturerName() const noexcept 31 | { 32 | return COMPANY_NAME_STR; 33 | } 34 | 35 | //-------------------------------------------------------------------- 36 | const char* ARAFactoryConfig::getInformationURL() const noexcept 37 | { 38 | return COMPANY_URL_STR; 39 | } 40 | 41 | //-------------------------------------------------------------------- 42 | const char* ARAFactoryConfig::getVersion() const noexcept 43 | { 44 | return VERSION_STR; 45 | } 46 | 47 | //-------------------------------------------------------------------- 48 | const char* ARAFactoryConfig::getDocumentArchiveID() const noexcept 49 | { 50 | return WORDIFY_DOCUMENT_ARCHIVE_ID; 51 | } 52 | 53 | //-------------------------------------------------------------------- 54 | } // namespace mam 55 | -------------------------------------------------------------------------------- /source/ara_factory_config.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "warn_cpp/suppress_warnings.h" 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "ARA_Library/PlugIn/ARAPlug.h" 8 | END_SUPPRESS_WARNINGS 9 | 10 | namespace mam { 11 | 12 | //-------------------------------------------------------------------- 13 | // ARAFactoryConfig 14 | //-------------------------------------------------------------------- 15 | class ARAFactoryConfig : public ARA::PlugIn::FactoryConfig 16 | { 17 | public: 18 | //---------------------------------------------------------------- 19 | const char* getFactoryID() const noexcept override; 20 | const char* getPlugInName() const noexcept override; 21 | const char* getManufacturerName() const noexcept override; 22 | const char* getInformationURL() const noexcept override; 23 | const char* getVersion() const noexcept override; 24 | const char* getDocumentArchiveID() const noexcept override; 25 | //---------------------------------------------------------------- 26 | }; 27 | 28 | //-------------------------------------------------------------------- 29 | } // namespace mam 30 | -------------------------------------------------------------------------------- /source/ara_main_factory.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! \file ARAMainFactory.h 3 | //! VST3 ARA Main Factory implementation for the ARA test plug-in. 4 | //! \project ARA SDK Examples 5 | //! \copyright Copyright (c) 2012-2023, Celemony Software GmbH, All Rights 6 | //! Reserved. \license Licensed under the Apache License, Version 2.0 (the 7 | //! "License"); 8 | //! you may not use this file except in compliance with the License. 9 | //! You may obtain a copy of the License at 10 | //! 11 | //! http://www.apache.org/licenses/LICENSE-2.0 12 | //! 13 | //! Unless required by applicable law or agreed to in writing, 14 | //! software distributed under the License is distributed on an "AS 15 | //! IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 16 | //! express or implied. See the License for the specific language 17 | //! governing permissions and limitations under the License. 18 | //------------------------------------------------------------------------------ 19 | 20 | #include "ARA_API/ARAVST3.h" 21 | #include "ara_document_controller.h" 22 | 23 | ARA_DISABLE_VST3_WARNINGS_BEGIN 24 | 25 | namespace mam { 26 | 27 | //------------------------------------------------------------------------ 28 | class ARAMainFactory : public ARA::IMainFactory 29 | { 30 | public: 31 | ARAMainFactory() { FUNKNOWN_CTOR } 32 | virtual ~ARAMainFactory() { FUNKNOWN_DTOR } 33 | 34 | // Class ID 35 | static const Steinberg::FUID getClassFUID() 36 | { 37 | return Steinberg::FUID(0xB761364A, 0x035149BF, 0xA580C576, 0xECD186FB); 38 | } 39 | 40 | // Create function 41 | static Steinberg::FUnknown* createInstance(void* /*context*/) 42 | { 43 | return (ARA::IMainFactory*)new ARAMainFactory(); 44 | } 45 | 46 | DECLARE_FUNKNOWN_METHODS 47 | 48 | //------------------------------------------------------------------------ 49 | // ARA::IMainFactory overrides: 50 | //------------------------------------------------------------------------ 51 | const ARA::ARAFactory* PLUGIN_API getFactory() SMTG_OVERRIDE 52 | { 53 | return ARADocumentController::getARAFactory(); 54 | } 55 | }; 56 | 57 | //------------------------------------------------------------------------ 58 | } // namespace mam 59 | 60 | IMPLEMENT_FUNKNOWN_METHODS(mam::ARAMainFactory, 61 | ARA::IMainFactory, 62 | ARA::IMainFactory::iid) 63 | 64 | ARA_DISABLE_VST3_WARNINGS_END 65 | -------------------------------------------------------------------------------- /source/audio_buffer_management.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace mam::audio_buffer_management { 10 | 11 | //------------------------------------------------------------------------ 12 | template 13 | using AudioBuffer = typename std::vector; 14 | 15 | template 16 | using MultiChannelBuffers = std::vector>; 17 | 18 | using MultiChannelData = std::vector; 19 | 20 | //------------------------------------------------------------------------ 21 | template 22 | auto create_channel_buffer(size_t num_samples) -> AudioBuffer 23 | { 24 | AudioBuffer audio_buf; 25 | audio_buf.resize(num_samples); 26 | return audio_buf; 27 | } 28 | 29 | //------------------------------------------------------------------------ 30 | template 31 | auto create_multi_channel_buffers(size_t num_channels, 32 | size_t num_samples) -> MultiChannelBuffers 33 | { 34 | MultiChannelBuffers multi_channel_bufs; 35 | multi_channel_bufs.resize(num_channels); 36 | for (auto& channel : multi_channel_bufs) 37 | { 38 | channel.resize(num_samples); 39 | } 40 | 41 | return multi_channel_bufs; 42 | } 43 | 44 | //------------------------------------------------------------------------ 45 | template 46 | auto to_channel_data(MultiChannelBuffers& multi_channel_bufs, 47 | size_t sample_start = 0) -> MultiChannelData 48 | { 49 | MultiChannelData channel_data; 50 | channel_data.reserve(multi_channel_bufs.size()); 51 | for (size_t ch = 0; ch < multi_channel_bufs.size(); ++ch) 52 | channel_data.push_back( 53 | static_cast(multi_channel_bufs[ch].data() + sample_start)); 54 | 55 | return channel_data; 56 | } 57 | 58 | //------------------------------------------------------------------------ 59 | template 60 | auto to_interleaved(const MultiChannelBuffers& multi_channel_bufs) 61 | -> AudioBuffer 62 | { 63 | AudioBuffer interleaved_buf; 64 | if (multi_channel_bufs.empty()) 65 | return interleaved_buf; 66 | 67 | const auto num_channels = multi_channel_bufs.size(); 68 | const auto num_samples = multi_channel_bufs[0].size(); 69 | 70 | for (size_t sample = 0; sample < num_samples; sample++) 71 | for (size_t channel = 0; channel < num_channels; channel++) 72 | interleaved_buf.push_back(multi_channel_bufs[channel][sample]); 73 | 74 | return interleaved_buf; 75 | } 76 | 77 | //------------------------------------------------------------------------ 78 | } // namespace mam::audio_buffer_management 79 | -------------------------------------------------------------------------------- /source/controllers/list_controller.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "search_engine.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | #include "wordify_types.h" 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "ara_document_controller.h" 10 | #include "base/source/fobject.h" 11 | #include "vstgui/lib/iviewlistener.h" 12 | #include "vstgui/uidescription/icontroller.h" 13 | END_SUPPRESS_WARNINGS 14 | 15 | namespace VSTGUI { 16 | class CRowColumnView; 17 | class IUIDescription; 18 | } // namespace VSTGUI 19 | 20 | namespace mam { 21 | namespace meta_words { 22 | class PlaybackRegion; 23 | } 24 | //------------------------------------------------------------------------ 25 | // ListController 26 | //------------------------------------------------------------------------ 27 | class ListController : public Steinberg::FObject, 28 | public VSTGUI::IController, 29 | public VSTGUI::ViewListenerAdapter 30 | { 31 | public: 32 | //-------------------------------------------------------------------- 33 | using PlaybackRegion = meta_words::PlaybackRegion; 34 | using Control = VSTGUI::CControl; 35 | using View = VSTGUI::CView; 36 | using RowColumnView = VSTGUI::CRowColumnView; 37 | using UIAttributes = VSTGUI::UIAttributes; 38 | using IUIDescription = VSTGUI::IUIDescription; 39 | using UTF8StringPtr = VSTGUI::UTF8StringPtr; 40 | using IController = VSTGUI::IController; 41 | using OptPlaybackRegionId = std::optional; 42 | 43 | ListController(ARADocumentController* document_controller, 44 | const IUIDescription* uidesc); 45 | ~ListController(); 46 | 47 | void PLUGIN_API update(FUnknown* /*changedUnknown*/, 48 | Steinberg::int32 /*message*/) override {}; 49 | View* verifyView(View* view, 50 | const UIAttributes& attributes, 51 | const IUIDescription* description) override; 52 | 53 | // IControlListener 54 | void valueChanged(Control* /*pControl*/) override {}; 55 | void controlBeginEdit(Control* /*pControl*/) override {}; 56 | void controlEndEdit(Control* /*pControl*/) override {}; 57 | IController* 58 | createSubController(UTF8StringPtr name, 59 | const IUIDescription* description) override; 60 | 61 | void viewWillDelete(VSTGUI::CView* view) override; 62 | 63 | OBJ_METHODS(ListController, FObject) 64 | 65 | //-------------------------------------------------------------------- 66 | private: 67 | void on_focus_word(const SearchEngine::SearchResult& search_result); 68 | void on_add_remove_playback_region(const RegionLifetimeEventData& data); 69 | void on_playback_regions_reordered(); 70 | void on_region_selected_by_host(Id region_id); 71 | auto create_list_item_view(const Id id) -> VSTGUI::CView*; 72 | 73 | RowColumnView* rowColView = nullptr; 74 | ARADocumentController* document_controller = nullptr; 75 | const IUIDescription* uidesc = nullptr; 76 | OptPlaybackRegionId playback_region_id; 77 | 78 | RegionLifetimeCallback::Handle lifetime_observer_handle; 79 | RegionsOrderCallback::Handle order_observer_handle; 80 | RegionSelectedByHostCallback::Handle region_selected_by_host_handle; 81 | SearchEngine::SearchEngineCallback::Handle focus_word_observer_handle; 82 | }; 83 | 84 | //------------------------------------------------------------------------ 85 | } // namespace mam 86 | -------------------------------------------------------------------------------- /source/controllers/list_entry_controller.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "list_entry_controller.h" 6 | #include "meta_words_clip_controller.h" 7 | 8 | namespace mam { 9 | 10 | //------------------------------------------------------------------------ 11 | static auto build_meta_words_data(const ARADocumentController* controller, 12 | const meta_words::PlaybackRegion::Id id) 13 | -> const MetaWordsData 14 | { 15 | if (!controller) 16 | return {}; 17 | 18 | auto opt_region = controller->find_playback_region(id); 19 | if (!opt_region) 20 | return {}; 21 | 22 | return opt_region.value()->get_meta_words_data(); 23 | } 24 | 25 | //------------------------------------------------------------------------ 26 | // ListEntryController 27 | //------------------------------------------------------------------------ 28 | ListEntryController::ListEntryController( 29 | ARADocumentController* controller, 30 | const meta_words::PlaybackRegion::Id id, 31 | const VSTGUI::IUIDescription* description) 32 | : controller(controller) 33 | , playback_region_id(id) 34 | , description(description) 35 | { 36 | } 37 | 38 | //------------------------------------------------------------------------ 39 | ListEntryController::~ListEntryController(){ 40 | 41 | }; 42 | 43 | //------------------------------------------------------------------------ 44 | VSTGUI::CView* 45 | ListEntryController::verifyView(VSTGUI::CView* view, 46 | const VSTGUI::UIAttributes& /*attributes*/, 47 | const VSTGUI::IUIDescription* /*description*/) 48 | { 49 | return view; 50 | }; 51 | 52 | //------------------------------------------------------------------------ 53 | VSTGUI::IController* ListEntryController::createSubController( 54 | VSTGUI::UTF8StringPtr name, const VSTGUI::IUIDescription* description_) 55 | { 56 | if (playback_region_id == meta_words::PlaybackRegion::INVALID_ID) 57 | return nullptr; 58 | 59 | if (!controller) 60 | return nullptr; 61 | 62 | auto pbr_id = playback_region_id; 63 | auto ctler = this->controller; 64 | 65 | if (VSTGUI::UTF8StringView(name) == "MetaWordsClipController") 66 | { 67 | auto* subctrl = new MetaWordsClipController(description); 68 | if (!subctrl) 69 | return nullptr; 70 | 71 | auto& subject = controller->get_playback_region_changed_subject(pbr_id); 72 | 73 | subctrl->meta_words_data_func = [=]() { 74 | return build_meta_words_data(ctler, pbr_id); 75 | }; 76 | 77 | subctrl->on_select_word_func = [=](int index) { 78 | controller->get_region_selection_model().select( 79 | {pbr_id, static_cast(index)}); 80 | controller->onRequestSelectWord(index, pbr_id); 81 | }; 82 | 83 | return subctrl->initialize(&subject) ? subctrl : nullptr; 84 | } 85 | 86 | return nullptr; 87 | } 88 | 89 | //------------------------------------------------------------------------ 90 | 91 | } // namespace mam 92 | -------------------------------------------------------------------------------- /source/controllers/list_entry_controller.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "ara_document_controller.h" 8 | #include "base/source/fobject.h" 9 | #include "tiny_observer_pattern.h" 10 | #include "vstgui/uidescription/icontroller.h" 11 | 12 | namespace mam { 13 | 14 | //------------------------------------------------------------------------ 15 | // ListEntryController 16 | //------------------------------------------------------------------------ 17 | class ListEntryController : public Steinberg::FObject, 18 | public VSTGUI::IController 19 | { 20 | public: 21 | //-------------------------------------------------------------------- 22 | ListEntryController(ARADocumentController* controller, 23 | const meta_words::PlaybackRegion::Id id, 24 | const VSTGUI::IUIDescription* description); 25 | 26 | ~ListEntryController() override; 27 | 28 | VSTGUI::CView* 29 | verifyView(VSTGUI::CView* view, 30 | const VSTGUI::UIAttributes& attributes, 31 | const VSTGUI::IUIDescription* description) override; 32 | VSTGUI::IController* 33 | createSubController(VSTGUI::UTF8StringPtr name, 34 | const VSTGUI::IUIDescription* description) override; 35 | 36 | void valueChanged(VSTGUI::CControl* pControl) override {} 37 | 38 | OBJ_METHODS(ListEntryController, FObject) 39 | //-------------------------------------------------------------------- 40 | private: 41 | const VSTGUI::IUIDescription* description = nullptr; 42 | ARADocumentController* controller = nullptr; 43 | const meta_words::PlaybackRegion::Id playback_region_id = 0; 44 | }; 45 | 46 | //------------------------------------------------------------------------ 47 | } // namespace mam 48 | -------------------------------------------------------------------------------- /source/controllers/preferences_controller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "preferences_controller.h" 4 | #include "ara_document_controller.h" 5 | #include "version.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | #include 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "public.sdk/source/vst/vstparameters.h" 10 | #include "vstgui/lib/cfileselector.h" 11 | #include "vstgui/lib/controls/coptionmenu.h" 12 | #include "vstgui/uidescription/uiattributes.h" 13 | END_SUPPRESS_WARNINGS 14 | 15 | #include 16 | #include 17 | 18 | #ifdef WIN32 19 | #include 20 | 21 | #include "utf_8_everywhere/convert.h" 22 | #include 23 | #endif 24 | 25 | namespace mam { 26 | using namespace VSTGUI; 27 | 28 | //------------------------------------------------------------------------ 29 | using URL = const struct 30 | { 31 | StringType value; 32 | }; 33 | 34 | auto open_url(URL& url) -> void 35 | { 36 | #if defined(WIN32) 37 | std::wstring wurl = utf_8_everywhere::convert(url.value); 38 | ShellExecuteW(0, L"open", wurl.c_str(), NULL, NULL, SW_SHOWDEFAULT); 39 | #elif __APPLE__ 40 | system((std::string("open ") + url.value).c_str()); 41 | #elif __linux__ 42 | system((std::string("xdg-open ") + url.value).c_str()); 43 | #endif 44 | } 45 | 46 | //------------------------------------------------------------------------ 47 | // PreferencesController 48 | //------------------------------------------------------------------------ 49 | PreferencesController::PreferencesController(ARADocumentController* controller) 50 | : controller(controller) 51 | { 52 | if (!controller) 53 | return; 54 | } 55 | 56 | //------------------------------------------------------------------------ 57 | PreferencesController::~PreferencesController() 58 | { 59 | if (options_menu) 60 | { 61 | options_menu->unregisterControlListener(this); 62 | options_menu->unregisterViewListener(this); 63 | options_menu = nullptr; 64 | } 65 | } 66 | 67 | //------------------------------------------------------------------------ 68 | CView* PreferencesController::verifyView(CView* view, 69 | const UIAttributes& attributes, 70 | const IUIDescription* /*description*/) 71 | { 72 | 73 | if (const auto* view_name = attributes.getAttributeValue("uidesc-label")) 74 | { 75 | if (*view_name == "PreferencesMenu") 76 | { 77 | options_menu = dynamic_cast(view); 78 | if (options_menu) 79 | { 80 | options_menu->addEntry("Visit wordify.org ..."); 81 | options_menu->addEntry("Export as Text ..."); 82 | options_menu->addEntry(UTF8String("v") + VERSION_STR, -1, 83 | CMenuItem::kDisabled); 84 | options_menu->registerControlListener(this); 85 | options_menu->registerViewListener(this); 86 | } 87 | } 88 | } 89 | 90 | return view; 91 | } 92 | 93 | //------------------------------------------------------------------------ 94 | void PreferencesController::valueChanged(CControl* pControl) 95 | { 96 | if (pControl == options_menu) 97 | { 98 | if (options_menu->getLastResult() == 0) 99 | { 100 | open_url(URL{"https://www.wordify.org"}); 101 | } 102 | else if (options_menu->getLastResult() == 1) 103 | { 104 | export_text(); 105 | } 106 | } 107 | } 108 | 109 | //------------------------------------------------------------------------ 110 | void PreferencesController::export_text() 111 | { 112 | nlohmann::json transcript; 113 | 114 | CNewFileSelector* selector = CNewFileSelector::create( 115 | options_menu->getFrame(), CNewFileSelector::kSelectSaveFile); 116 | if (selector) 117 | { 118 | selector->setTitle("Save JSON File"); 119 | selector->setAllowMultiFileSelection(false); 120 | selector->setDefaultExtension(CFileExtension("JSON", "json")); 121 | selector->run([&](CNewFileSelector* control) { 122 | if (control == nullptr) 123 | return; 124 | 125 | auto size = control->getNumSelectedFiles(); 126 | if (size == 0) 127 | { 128 | std::cerr << "No file selected!" << std::endl; 129 | return; 130 | } 131 | 132 | std::string filePath = control->getSelectedFile(0); 133 | if (!filePath.empty()) 134 | { 135 | controller->for_each_region_id([&](const Id id) { 136 | add_transcript_to_json(id, transcript); 137 | }); 138 | 139 | std::ofstream file(filePath); 140 | if (file.is_open()) 141 | { 142 | file << std::setw(4) << transcript << std::endl; 143 | file.close(); 144 | std::cout << "JSON file saved to: " << filePath 145 | << std::endl; 146 | } 147 | else 148 | { 149 | std::cerr << "Unable to open file for writing!" 150 | << std::endl; 151 | } 152 | } 153 | }); 154 | selector->forget(); 155 | } 156 | } 157 | 158 | //------------------------------------------------------------------------ 159 | void PreferencesController::add_transcript_to_json(const Id region_id, 160 | nlohmann::json& transcript) 161 | { 162 | if (!controller) 163 | return; 164 | 165 | auto opt_region = controller->find_playback_region(region_id); 166 | auto region = opt_region.value_or(nullptr); 167 | if (!region) 168 | return; 169 | 170 | const auto words_data = region->get_region_data(); 171 | 172 | StringType speaker = words_data.name; 173 | StringType start_time = std::to_string(words_data.project_time_start); 174 | StringType duration = std::to_string(words_data.duration); 175 | 176 | const auto& words = words_data.words; 177 | StringType spoken_text; 178 | for (const auto& word : words) 179 | spoken_text += word.word.value + " "; 180 | 181 | nlohmann::json speaker_data = {{"speaker", speaker}, 182 | {"start_time", start_time}, 183 | {"duration", duration}, 184 | {"spoken_text", spoken_text}}; 185 | 186 | transcript["transcript"].push_back(speaker_data); 187 | } 188 | 189 | //------------------------------------------------------------------------ 190 | void PLUGIN_API PreferencesController::update(FUnknown* /*changedUnknown*/, 191 | Steinberg::int32 /*tag*/) 192 | { 193 | } 194 | 195 | //------------------------------------------------------------------------ 196 | void PreferencesController::viewWillDelete(VSTGUI::CView* view) 197 | { 198 | if (view == options_menu) 199 | { 200 | options_menu->unregisterControlListener(this); 201 | options_menu->unregisterViewListener(this); 202 | options_menu = nullptr; 203 | } 204 | } 205 | 206 | //------------------------------------------------------------------------ 207 | } // namespace mam 208 | -------------------------------------------------------------------------------- /source/controllers/preferences_controller.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "warn_cpp/suppress_warnings.h" 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "base/source/fobject.h" 8 | #include "vstgui/lib/iviewlistener.h" 9 | #include "vstgui/uidescription/icontroller.h" 10 | END_SUPPRESS_WARNINGS 11 | 12 | #include "nlohmann/json.hpp" 13 | #include "wordify_types.h" 14 | 15 | namespace VSTGUI { 16 | class COptionMenu; 17 | } 18 | 19 | namespace mam { 20 | class ARADocumentController; 21 | 22 | //------------------------------------------------------------------------ 23 | // PreferencesController 24 | //------------------------------------------------------------------------ 25 | class PreferencesController : public Steinberg::FObject, 26 | public VSTGUI::IController, 27 | public VSTGUI::ViewListenerAdapter 28 | { 29 | public: 30 | //-------------------------------------------------------------------- 31 | using SchemeToggle = std::function; 32 | 33 | PreferencesController(ARADocumentController* controller); 34 | ~PreferencesController() override; 35 | 36 | // IController 37 | void PLUGIN_API update(FUnknown* changedUnknown, 38 | Steinberg::int32 message) override; 39 | VSTGUI::CView* 40 | verifyView(VSTGUI::CView* view, 41 | const VSTGUI::UIAttributes& attributes, 42 | const VSTGUI::IUIDescription* description) override; 43 | 44 | // IControlListener 45 | void valueChanged(VSTGUI::CControl* pControl) override; 46 | 47 | // IViewListener 48 | void viewWillDelete(VSTGUI::CView* view) override; 49 | 50 | OBJ_METHODS(PreferencesController, FObject) 51 | 52 | //-------------------------------------------------------------------- 53 | private: 54 | void export_text(); 55 | void add_transcript_to_json(const Id region_id, nlohmann::json& transcript); 56 | ARADocumentController* controller = nullptr; 57 | VSTGUI::COptionMenu* options_menu = nullptr; 58 | }; 59 | 60 | //------------------------------------------------------------------------ 61 | } // namespace mam 62 | -------------------------------------------------------------------------------- /source/controllers/region_controller.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "region_data.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | BEGIN_SUPPRESS_WARNINGS 8 | #include "base/source/fobject.h" 9 | #include "eventpp/callbacklist.h" 10 | #include "vstgui/lib/iviewlistener.h" 11 | #include "vstgui/uidescription/icontroller.h" 12 | #include "vstgui/uidescription/uiattributes.h" 13 | END_SUPPRESS_WARNINGS 14 | 15 | namespace VSTGUI { 16 | class IUIDescription; 17 | class CTextLabel; 18 | class CTextLabel; 19 | class CViewContainer; 20 | class CView; 21 | }; // namespace VSTGUI 22 | 23 | namespace mam { 24 | 25 | class HStackLayout; 26 | 27 | //------------------------------------------------------------------------ 28 | // RegionController 29 | //------------------------------------------------------------------------ 30 | class RegionController : public Steinberg::FObject, 31 | public VSTGUI::IController, 32 | public VSTGUI::ViewListenerAdapter 33 | { 34 | public: 35 | //-------------------------------------------------------------------- 36 | using FuncRegionData = std::function; 37 | using FuncOnSelectedWord = std::function; 38 | using Subject = eventpp::CallbackList; 39 | using ObserverHandle = Subject::Handle; 40 | using Width = VSTGUI::CCoord; 41 | 42 | struct Cache 43 | { 44 | using Widths = std::vector; 45 | Widths word_widths; 46 | }; 47 | 48 | RegionController(const VSTGUI::IUIDescription* description); 49 | ~RegionController() override; 50 | 51 | bool initialize(Subject* subject); 52 | 53 | VSTGUI::CView* 54 | verifyView(VSTGUI::CView* view, 55 | const VSTGUI::UIAttributes& attributes, 56 | const VSTGUI::IUIDescription* description) override; 57 | 58 | // IControlListener 59 | void valueChanged(VSTGUI::CControl* pControl) override; 60 | 61 | // ViewListenerAdapter 62 | void viewAttached(VSTGUI::CView* view) override; 63 | void viewRemoved(VSTGUI::CView* view) override; 64 | void viewWillDelete(VSTGUI::CView* view) override; 65 | 66 | FuncOnSelectedWord on_select_word_func; 67 | FuncRegionData region_data_func; 68 | 69 | OBJ_METHODS(RegionController, FObject) 70 | //-------------------------------------------------------------------- 71 | private: 72 | void on_region_changed(); 73 | void init_words_width_cache(const RegionData& data); 74 | 75 | const VSTGUI::IUIDescription* description = nullptr; 76 | VSTGUI::CTextLabel* region_title = nullptr; 77 | VSTGUI::CTextLabel* region_start_time = nullptr; 78 | VSTGUI::CTextLabel* region_duration_time = nullptr; 79 | VSTGUI::CViewContainer* region_transcript = nullptr; 80 | 81 | std::unique_ptr stack_layout; 82 | VSTGUI::UIAttributes meta_word_button_attributes; 83 | 84 | Subject* subject = nullptr; 85 | ObserverHandle observer_handle; 86 | Cache cache; 87 | }; 88 | //------------------------------------------------------------------------ 89 | } // namespace mam 90 | -------------------------------------------------------------------------------- /source/controllers/search_controller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "search_controller.h" 4 | #include "ara_document_controller.h" 5 | #include "search_engine.h" 6 | #include "string_matcher.h" 7 | #include "warn_cpp/suppress_warnings.h" 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "public.sdk/source/vst/vstparameters.h" 10 | #include "vstgui/lib/controls/csearchtextedit.h" 11 | #include "vstgui/uidescription/iuidescription.h" 12 | #include "vstgui/uidescription/uiattributes.h" 13 | END_SUPPRESS_WARNINGS 14 | 15 | using namespace VSTGUI; 16 | using namespace Steinberg; 17 | 18 | namespace mam { 19 | 20 | //------------------------------------------------------------------------ 21 | enum 22 | { 23 | kSearchFieldTag = 1000, 24 | }; 25 | 26 | //------------------------------------------------------------------------ 27 | auto string_match_method(Steinberg::Vst::Parameter* smart_search_param) 28 | { 29 | bool mode = smart_search_param->getNormalized() > 0.; 30 | return mode ? StringMatcher::MatchMethod::nearbyFuzzyMatch 31 | : StringMatcher::MatchMethod::directMatch; 32 | } 33 | 34 | //------------------------------------------------------------------------ 35 | // SearchController 36 | //------------------------------------------------------------------------ 37 | SearchController::SearchController( 38 | ARADocumentController* controller, 39 | Steinberg::Vst::Parameter* smart_search_param) 40 | : controller(controller) 41 | , smart_search_param(smart_search_param) 42 | { 43 | if (!controller) 44 | return; 45 | 46 | SearchEngine::instance().get_regions = [controller]() { 47 | return controller->get_playback_regions(); 48 | }; 49 | 50 | if (smart_search_param) 51 | smart_search_param->addDependent(this); 52 | 53 | region_props_changed_observer_handle = 54 | controller->get_region_changed_subject().append( 55 | [&](const auto&) { clear_search(); }); 56 | 57 | region_lifetime_observer_handle = 58 | controller->get_playback_region_lifetimes_subject()->append( 59 | [&](const auto&) { clear_search(); }); 60 | } 61 | 62 | //------------------------------------------------------------------------ 63 | SearchController::~SearchController() 64 | { 65 | if (search_field) 66 | { 67 | search_field->unregisterViewListener(this); 68 | search_field = nullptr; 69 | } 70 | 71 | if (controller) 72 | { 73 | controller->get_region_changed_subject().remove( 74 | region_props_changed_observer_handle); 75 | 76 | controller->get_playback_region_lifetimes_subject()->remove( 77 | region_lifetime_observer_handle); 78 | } 79 | 80 | if (smart_search_param) 81 | smart_search_param->removeDependent(this); 82 | 83 | if (SearchEngine::instance().get_regions) 84 | SearchEngine::instance().get_regions = nullptr; 85 | } 86 | 87 | //------------------------------------------------------------------------ 88 | void PLUGIN_API SearchController::update(FUnknown* changedUnknown, 89 | Steinberg::int32 /*message*/) 90 | { 91 | if (auto* param = FCast(changedUnknown)) 92 | { 93 | if (param == smart_search_param) 94 | { 95 | const auto smm = string_match_method(smart_search_param); 96 | SearchEngine::instance().research( 97 | [&](const auto& s0, const auto& s1) -> bool { 98 | return StringMatcher::isMatch(s0, s1, smm); 99 | }); 100 | } 101 | } 102 | } 103 | 104 | //------------------------------------------------------------------------ 105 | CView* SearchController::createView(const UIAttributes& /*attributes*/, 106 | const IUIDescription* /*description*/) 107 | { 108 | return nullptr; 109 | } 110 | 111 | //------------------------------------------------------------------------ 112 | CView* SearchController::verifyView(CView* view, 113 | const UIAttributes& attributes, 114 | const IUIDescription* /*description*/) 115 | { 116 | if (const auto* view_name = attributes.getAttributeValue("uidesc-label")) 117 | { 118 | if (*view_name == "Search") 119 | { 120 | if (auto c = dynamic_cast(view)) 121 | { 122 | search_field = c; 123 | c->setText(SearchEngine::instance().current_search_word()); 124 | c->setTag(kSearchFieldTag); 125 | c->setListener(this); 126 | c->registerViewListener(this); 127 | } 128 | } 129 | } 130 | 131 | return view; 132 | } 133 | 134 | //------------------------------------------------------------------------ 135 | IController* 136 | SearchController::createSubController(UTF8StringPtr /*name*/, 137 | const IUIDescription* /*description*/) 138 | { 139 | return nullptr; 140 | } 141 | 142 | //------------------------------------------------------------------------ 143 | void SearchController::valueChanged(CControl* control) 144 | { 145 | switch (control->getTag()) 146 | { 147 | case kSearchFieldTag: { 148 | if (auto sf = dynamic_cast(control)) 149 | { 150 | const auto search_word = sf->getText().getString(); 151 | if (search_word.empty()) 152 | SearchEngine::instance().clear_results(); 153 | else 154 | { 155 | const auto smm = string_match_method(smart_search_param); 156 | SearchEngine::instance().search( 157 | search_word, 158 | [&](const auto& s0, const auto& s1) -> bool { 159 | return StringMatcher::isMatch(s0, s1, smm); 160 | }); 161 | } 162 | } 163 | 164 | break; 165 | } 166 | } 167 | } 168 | 169 | //------------------------------------------------------------------------ 170 | void SearchController::viewWillDelete(VSTGUI::CView* view) 171 | { 172 | if (search_field == view) 173 | { 174 | search_field->unregisterViewListener(this); 175 | search_field = nullptr; 176 | } 177 | } 178 | 179 | //------------------------------------------------------------------------ 180 | void SearchController::clear_search() 181 | { 182 | if (search_field) 183 | search_field->setText(UTF8String("")); 184 | 185 | SearchEngine::instance().clear_results(); 186 | } 187 | 188 | //------------------------------------------------------------------------ 189 | } // namespace mam 190 | -------------------------------------------------------------------------------- /source/controllers/search_controller.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "ara_document_controller.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | BEGIN_SUPPRESS_WARNINGS 8 | #include "base/source/fobject.h" 9 | #include "vstgui/lib/iviewlistener.h" 10 | #include "vstgui/uidescription/icontroller.h" 11 | END_SUPPRESS_WARNINGS 12 | 13 | namespace VSTGUI { 14 | class CSearchTextEdit; 15 | } // namespace VSTGUI 16 | 17 | namespace Steinberg::Vst { 18 | class Parameter; 19 | }; // namespace Steinberg::Vst 20 | 21 | namespace mam { 22 | 23 | //------------------------------------------------------------------------ 24 | // SearchController 25 | //------------------------------------------------------------------------ 26 | class SearchController : public Steinberg::FObject, 27 | public VSTGUI::IController, 28 | public VSTGUI::ViewListenerAdapter 29 | { 30 | public: 31 | //-------------------------------------------------------------------- 32 | SearchController(ARADocumentController* controller, 33 | Steinberg::Vst::Parameter* smart_search_param); 34 | ~SearchController() override; 35 | 36 | void PLUGIN_API update(FUnknown* changedUnknown, 37 | Steinberg::int32 message) override; 38 | 39 | VSTGUI::CView* 40 | createView(const VSTGUI::UIAttributes& attributes, 41 | const VSTGUI::IUIDescription* description) override; 42 | 43 | VSTGUI::CView* 44 | verifyView(VSTGUI::CView* view, 45 | const VSTGUI::UIAttributes& attributes, 46 | const VSTGUI::IUIDescription* description) override; 47 | 48 | // IControlListener 49 | void valueChanged(VSTGUI::CControl* pControl) override; 50 | VSTGUI::IController* 51 | createSubController(VSTGUI::UTF8StringPtr name, 52 | const VSTGUI::IUIDescription* description) override; 53 | 54 | // ViewListenerAdapter 55 | void viewWillDelete(VSTGUI::CView* view) override; 56 | 57 | OBJ_METHODS(SearchController, FObject) 58 | 59 | //-------------------------------------------------------------------- 60 | private: 61 | ARADocumentController* controller = nullptr; 62 | Steinberg::Vst::Parameter* smart_search_param = nullptr; 63 | VSTGUI::CSearchTextEdit* search_field = nullptr; 64 | 65 | RegionChangedCallback::Handle region_props_changed_observer_handle; 66 | RegionLifetimeCallback::Handle region_lifetime_observer_handle; 67 | 68 | void clear_search(); 69 | }; 70 | 71 | //------------------------------------------------------------------------ 72 | } // namespace mam 73 | -------------------------------------------------------------------------------- /source/controllers/spinner_controller.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "ara_document_controller.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | #include "wordify_types.h" 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "base/source/fobject.h" 10 | #include "vstgui/lib/iviewlistener.h" 11 | #include "vstgui/uidescription/icontroller.h" 12 | END_SUPPRESS_WARNINGS 13 | 14 | namespace VSTGUI { 15 | class CViewContainer; 16 | } // namespace VSTGUI 17 | 18 | namespace Steinberg::Vst { 19 | class Parameter; 20 | } 21 | 22 | namespace mam { 23 | class SpinnerView; 24 | 25 | //------------------------------------------------------------------------ 26 | // SpinnerController 27 | //------------------------------------------------------------------------ 28 | class SpinnerController : public Steinberg::FObject, 29 | public VSTGUI::IController, 30 | public VSTGUI::ViewListenerAdapter 31 | { 32 | public: 33 | //-------------------------------------------------------------------- 34 | using TextLabel = VSTGUI::CTextLabel; 35 | using ViewContainer = VSTGUI::CViewContainer; 36 | 37 | SpinnerController(ARADocumentController* controller, 38 | Steinberg::Vst::Parameter* param); 39 | ~SpinnerController() override; 40 | 41 | void PLUGIN_API update(FUnknown* changedUnknown, 42 | Steinberg::int32 message) override; 43 | 44 | VSTGUI::CView* 45 | createView(const VSTGUI::UIAttributes& attributes, 46 | const VSTGUI::IUIDescription* description) override; 47 | 48 | VSTGUI::CView* 49 | verifyView(VSTGUI::CView* view, 50 | const VSTGUI::UIAttributes& attributes, 51 | const VSTGUI::IUIDescription* description) override; 52 | 53 | // IControlListener 54 | void valueChanged(VSTGUI::CControl* /*pControl*/) override {} 55 | VSTGUI::IController* 56 | createSubController(VSTGUI::UTF8StringPtr name, 57 | const VSTGUI::IUIDescription* description) override; 58 | 59 | // IViewListener 60 | void viewWillDelete(VSTGUI::CView* view) override; 61 | void viewAttached(VSTGUI::CView* view) override; 62 | 63 | OBJ_METHODS(SpinnerController, FObject) 64 | 65 | //-------------------------------------------------------------------- 66 | private: 67 | size_t count_tasks() const; 68 | void on_task_count_changed(); 69 | void on_task_count_changed(size_t value, const StringType& value_str); 70 | 71 | ARADocumentController* controller = nullptr; 72 | ViewContainer* spinner_layout = nullptr; 73 | TextLabel* spinner_badge = nullptr; 74 | SpinnerView* spinner_view = nullptr; 75 | Steinberg::IPtr task_counter; 76 | }; 77 | 78 | //------------------------------------------------------------------------ 79 | } // namespace mam 80 | -------------------------------------------------------------------------------- /source/controllers/waveform_controller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "waveform_controller.h" 4 | #include 5 | BEGIN_SUPPRESS_WARNINGS 6 | #include "vstgui/lib/cgradientview.h" 7 | #include "vstgui/uidescription/uiattributes.h" 8 | END_SUPPRESS_WARNINGS 9 | 10 | namespace mam { 11 | using namespace ::VSTGUI; 12 | 13 | //------------------------------------------------------------------------ 14 | static auto 15 | read_view_size(const VSTGUI::UIAttributes& attributes) -> VSTGUI::CPoint 16 | { 17 | VSTGUI::CPoint size; 18 | attributes.getPointAttribute("size", size); 19 | return size; 20 | } 21 | 22 | //------------------------------------------------------------------------ 23 | static auto update_background_view(CGradientView* view, 24 | const WaveFormController::Data& /*data*/) -> void 25 | { 26 | if (!view) 27 | return; 28 | 29 | // TODO: Create a new gradient and set it to the view! 30 | // view->setGradient(new VSTGUI::CGradient(...)); 31 | // view->invalid(); 32 | } 33 | 34 | //------------------------------------------------------------------------ 35 | // WaveFormController 36 | //------------------------------------------------------------------------ 37 | WaveFormController::WaveFormController(ARADocumentController* controller) 38 | : controller(controller) 39 | { 40 | } 41 | 42 | //------------------------------------------------------------------------ 43 | WaveFormController::~WaveFormController() 44 | { 45 | if (subject) 46 | subject->remove(observer_handle); 47 | 48 | unregister_current_region_props_observer(); 49 | 50 | if (waveform_view) 51 | waveform_view->unregisterViewListener(this); 52 | 53 | if (background_view) 54 | background_view->unregisterViewListener(this); 55 | } 56 | 57 | //------------------------------------------------------------------------ 58 | bool WaveFormController::initialize(Subject* _subject, FuncWaveFormData&& func) 59 | { 60 | if (!_subject) 61 | return false; 62 | 63 | subject = _subject; 64 | waveform_data_func = std::move(func); 65 | observer_handle = subject->append( 66 | [&](const auto& data) { this->on_selected_region_word(data); }); 67 | 68 | return true; 69 | } 70 | 71 | //------------------------------------------------------------------------ 72 | void WaveFormController::on_selected_region_word( 73 | const SelectedWordEventData& new_selected_word) 74 | { 75 | if (selected_word.region_id != new_selected_word.region_id) 76 | { 77 | unregister_current_region_props_observer(); 78 | register_region_props_observer(new_selected_word); 79 | } 80 | 81 | update_waveform(); 82 | } 83 | 84 | //------------------------------------------------------------------------ 85 | CView* WaveFormController::createView(const VSTGUI::UIAttributes& attributes, 86 | const VSTGUI::IUIDescription* /*description*/) 87 | { 88 | if (const auto* view_name = 89 | attributes.getAttributeValue("custom-view-name")) 90 | { 91 | if (*view_name == "WaveForm") 92 | { 93 | const auto view_size = read_view_size(attributes); 94 | return new WaveFormView(CRect{0, 0, view_size.x, view_size.y}); 95 | } 96 | } 97 | 98 | return nullptr; 99 | } 100 | 101 | //------------------------------------------------------------------------ 102 | VSTGUI::CView* 103 | WaveFormController::verifyView(VSTGUI::CView* view, 104 | const VSTGUI::UIAttributes& attributes, 105 | const VSTGUI::IUIDescription* /*description*/) 106 | { 107 | if (const auto* view_name = 108 | attributes.getAttributeValue("custom-view-name")) 109 | { 110 | if (*view_name == "WaveForm") 111 | { 112 | waveform_view = dynamic_cast(view); 113 | waveform_view->waveform_data_func = this->waveform_data_func; 114 | waveform_view->registerViewListener(this); 115 | } 116 | else if (*view_name == "Background") 117 | { 118 | background_view = dynamic_cast(view); 119 | background_view->registerViewListener(this); 120 | } 121 | } 122 | return view; 123 | } 124 | 125 | //------------------------------------------------------------------------ 126 | void WaveFormController::viewAttached(CView* view) 127 | { 128 | if (view == waveform_view) 129 | { 130 | waveform_view->invalid(); 131 | } 132 | else if (view == background_view) 133 | { 134 | update_background_view(background_view, waveform_data_func()); 135 | } 136 | } 137 | 138 | //------------------------------------------------------------------------ 139 | 140 | void WaveFormController::viewWillDelete(CView* view) 141 | { 142 | if (view == waveform_view) 143 | { 144 | waveform_view->unregisterViewListener(this); 145 | waveform_view = nullptr; 146 | } 147 | else if (view == background_view) 148 | { 149 | background_view->unregisterViewListener(this); 150 | background_view = nullptr; 151 | } 152 | } 153 | 154 | //------------------------------------------------------------------------ 155 | void WaveFormController::update_waveform() 156 | { 157 | if (waveform_view) 158 | waveform_view->invalid(); 159 | 160 | const auto data = this->waveform_data_func(); 161 | update_background_view(background_view, data); 162 | } 163 | 164 | //------------------------------------------------------------------------ 165 | void WaveFormController::register_region_props_observer( 166 | const SelectedWordEventData& new_selected_word) 167 | { 168 | if (!controller) 169 | return; 170 | 171 | selected_word = new_selected_word; 172 | 173 | region_props_observer_handle = 174 | controller->get_playback_region_changed_subject(selected_word.region_id) 175 | .append([&]() { update_waveform(); }); 176 | } 177 | 178 | //------------------------------------------------------------------------ 179 | void WaveFormController::unregister_current_region_props_observer() 180 | { 181 | if (!controller) 182 | return; 183 | 184 | controller->get_playback_region_changed_subject(selected_word.region_id) 185 | .remove(region_props_observer_handle); 186 | } 187 | 188 | //------------------------------------------------------------------------ 189 | } // namespace mam 190 | -------------------------------------------------------------------------------- /source/controllers/waveform_controller.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "ara_document_controller.h" 6 | #include "nonstd.h" 7 | #include "warn_cpp/suppress_warnings.h" 8 | #include "views/waveform_view.h" 9 | BEGIN_SUPPRESS_WARNINGS 10 | #include "base/source/fobject.h" 11 | #include "eventpp/callbacklist.h" 12 | #include "vstgui/lib/iviewlistener.h" 13 | #include "vstgui/uidescription/icontroller.h" 14 | END_SUPPRESS_WARNINGS 15 | 16 | namespace VSTGUI { 17 | class CListControl; 18 | class CGradientView; 19 | } // namespace VSTGUI 20 | 21 | namespace mam { 22 | 23 | //------------------------------------------------------------------------ 24 | // WaveFormController 25 | //------------------------------------------------------------------------ 26 | class WaveFormController : public Steinberg::FObject, 27 | public VSTGUI::IController, 28 | public VSTGUI::ViewListenerAdapter 29 | { 30 | public: 31 | //-------------------------------------------------------------------- 32 | using Subject = eventpp::CallbackList; 33 | using ObserverHandle = Subject::Handle; 34 | using Data = const WaveFormView::Data; 35 | 36 | using FuncWaveFormData = std::function; 37 | 38 | WaveFormController(ARADocumentController* controller); 39 | ~WaveFormController() override; 40 | 41 | bool initialize(Subject* subject, FuncWaveFormData&& waveform_data_func); 42 | 43 | void PLUGIN_API update(FUnknown* /*changedUnknown*/, 44 | Steinberg::int32 /*message*/) override {}; 45 | 46 | VSTGUI::CView* 47 | createView(const VSTGUI::UIAttributes& attributes, 48 | const VSTGUI::IUIDescription* description) override; 49 | 50 | VSTGUI::CView* 51 | verifyView(VSTGUI::CView* view, 52 | const VSTGUI::UIAttributes& attributes, 53 | const VSTGUI::IUIDescription* description) override; 54 | 55 | // IControlListener 56 | void valueChanged(VSTGUI::CControl* /*pControl*/) override {}; 57 | 58 | // ViewListenerAdapter 59 | void viewAttached(VSTGUI::CView* view) override; 60 | void viewWillDelete(VSTGUI::CView* view) override; 61 | 62 | OBJ_METHODS(WaveFormController, FObject) 63 | 64 | //-------------------------------------------------------------------- 65 | private: 66 | void on_selected_region_word(const SelectedWordEventData& selected_word); 67 | void update_waveform(); 68 | void 69 | register_region_props_observer(const SelectedWordEventData& selected_word); 70 | void unregister_current_region_props_observer(); 71 | 72 | ARADocumentController* controller = nullptr; 73 | Subject* subject = nullptr; 74 | ObserverHandle observer_handle; 75 | FuncWaveFormData waveform_data_func; 76 | WaveFormView* waveform_view = nullptr; 77 | VSTGUI::CGradientView* background_view = nullptr; 78 | 79 | RegionPropsChangedCallback::Handle region_props_observer_handle; 80 | SelectedWordEventData selected_word; 81 | }; 82 | 83 | //------------------------------------------------------------------------ 84 | } // namespace mam 85 | -------------------------------------------------------------------------------- /source/little_helpers.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "warn_cpp/suppress_warnings.h" 8 | #include "wordify_types.h" 9 | #include 10 | #include 11 | BEGIN_SUPPRESS_WARNINGS 12 | #include "fmt/format.h" 13 | #include "vstgui/lib/animation/animator.h" 14 | #include "vstgui/lib/ccolor.h" 15 | #include "vstgui/lib/cframe.h" 16 | END_SUPPRESS_WARNINGS 17 | 18 | namespace mam { 19 | 20 | //------------------------------------------------------------------------ 21 | // WaveFormView 22 | //------------------------------------------------------------------------ 23 | template 24 | class BoundsCheck 25 | { 26 | public: 27 | //-------------------------------------------------------------------- 28 | using Range = std::pair; 29 | BoundsCheck(const Range& range) 30 | : lo(range.first) 31 | , hi(range.first + range.second) 32 | { 33 | } 34 | 35 | bool is_in(const T& value) const { return is_in(value, lo, hi); } 36 | 37 | //-------------------------------------------------------------------- 38 | 39 | private: 40 | const T lo{0}; 41 | const T hi{0}; 42 | 43 | bool is_in(const T& value, const T& lo_, const T& hi_) const 44 | { 45 | return !(value < lo_) && !(hi_ < value); 46 | } 47 | }; 48 | 49 | //------------------------------------------------------------------------ 50 | template 51 | static auto make_color(const T& norm_r, 52 | const T& norm_g, 53 | const T& norm_b, 54 | std::optional opt_tint) -> const VSTGUI::CColor 55 | { 56 | const auto tint = opt_tint.value_or(0.f); 57 | VSTGUI::CColor color_normal; 58 | color_normal.setNormRed(norm_r + (T(1.) - norm_r) * tint); 59 | color_normal.setNormGreen(norm_g + (T(1.) - norm_g) * tint); 60 | color_normal.setNormBlue(norm_b + (T(1.) - norm_b) * tint); 61 | return color_normal; 62 | } 63 | 64 | //------------------------------------------------------------------------ 65 | inline StringType trim(const StringType& source) 66 | { 67 | StringType s(source); 68 | s.erase(0, s.find_first_not_of(" \n\r\t")); 69 | s.erase(s.find_last_not_of(" \n\r\t") + 1); 70 | return s; 71 | } 72 | 73 | //------------------------------------------------------------------------ 74 | using TimeDisplayString = StringType; 75 | template 76 | auto to_time_display_string(T seconds) -> TimeDisplayString 77 | { 78 | namespace chrono = std::chrono; 79 | 80 | chrono::seconds total(static_cast(seconds)); 81 | const auto h = chrono::duration_cast(total); 82 | const auto m = chrono::duration_cast(total - h); 83 | const auto s = chrono::duration_cast(total - h - m); 84 | 85 | // auto output = 86 | // fmt::format("{:02}:{:02}:{:02}", h.count(), m.count(), s.count()); 87 | 88 | auto output = fmt::format("{:02}:{:02}", m.count(), s.count()); 89 | 90 | // Only if there are some hours we display them 91 | if (h.count() > 0.) 92 | output = fmt::format("{:2}:{}", h.count(), output); 93 | 94 | return output; 95 | } 96 | 97 | //------------------------------------------------------------------------ 98 | #if __linux__ 99 | inline auto fix_crash_on_linux(VSTGUI::CView* view) -> void 100 | { 101 | /** 102 | * Temporary workaround: 103 | * By calling 'onTimer(...)' after removing the view animtation, the 104 | * animator will remove itself from the timer by using the platform 105 | * frame. If we don't do that here, there is an ASSERT hitting which 106 | * leads to a crash later. 107 | */ 108 | if (view->getFrame()->getAnimator()) 109 | view->getFrame()->getAnimator()->onTimer(); 110 | } 111 | #endif 112 | 113 | //------------------------------------------------------------------------ 114 | 115 | } // namespace mam 116 | -------------------------------------------------------------------------------- /source/meta_word_data.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-and-me/wordify-plugin/7f2ff02d39402d65163b65b5e0d7ccceac1413ef/source/meta_word_data.cpp -------------------------------------------------------------------------------- /source/meta_word_data.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright(c) 2023 Max And Me. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "mam/meta_words/meta_word.h" 6 | #include 7 | 8 | namespace mam { 9 | //------------------------------------------------------------------------ 10 | struct MetaWordData 11 | { 12 | bool is_audible = true; 13 | meta_words::MetaWord word; 14 | }; 15 | 16 | //------------------------------------------------------------------------ 17 | using MetaWordData = std::vector; 18 | //------------------------------------------------------------------------ 19 | 20 | } // namespace mam -------------------------------------------------------------------------------- /source/meta_words_audio_modification.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "meta_words_audio_modification.h" 6 | #include "ARA_Library/PlugIn/ARAPlug.h" 7 | 8 | namespace mam::meta_words { 9 | //------------------------------------------------------------------------ 10 | AudioModification::AudioModification( 11 | ARA::PlugIn::AudioSource* audioSource, 12 | ARA::ARAAudioModificationHostRef hostRef, 13 | const ARA::PlugIn::AudioModification* optionalModificationToClone) noexcept 14 | : ARA::PlugIn::AudioModification( 15 | audioSource, hostRef, optionalModificationToClone) 16 | { 17 | } 18 | //------------------------------------------------------------------------ 19 | } // namespace mam::meta_words -------------------------------------------------------------------------------- /source/meta_words_audio_modification.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "mam/meta_words/meta_word.h" 8 | #include "warn_cpp/suppress_warnings.h" 9 | BEGIN_SUPPRESS_WARNINGS 10 | #include "ARA_Library/PlugIn/ARAPlug.h" 11 | END_SUPPRESS_WARNINGS 12 | 13 | namespace mam::meta_words { 14 | 15 | //------------------------------------------------------------------------ 16 | class AudioModification : public ARA::PlugIn::AudioModification 17 | { 18 | public: 19 | //-------------------------------------------------------------------- 20 | AudioModification(ARA::PlugIn::AudioSource* audioSource, 21 | ARA::ARAAudioModificationHostRef hostRef, 22 | const ARA::PlugIn::AudioModification* 23 | optionalModificationToClone) noexcept; 24 | //-------------------------------------------------------------------- 25 | private: 26 | }; 27 | 28 | //------------------------------------------------------------------------ 29 | } // namespace mam::meta_words -------------------------------------------------------------------------------- /source/meta_words_audio_source.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "audio_buffer_management.h" 6 | #include "mam/meta_words/meta_word.h" 7 | #include "warn_cpp/suppress_warnings.h" 8 | #include "wordify_types.h" 9 | #include 10 | #include 11 | BEGIN_SUPPRESS_WARNINGS 12 | #include "ARA_Library/PlugIn/ARAPlug.h" 13 | #include "base/source/timer.h" 14 | END_SUPPRESS_WARNINGS 15 | 16 | namespace mam::meta_words { 17 | 18 | //------------------------------------------------------------------------ 19 | // AnalyseProgressData 20 | //------------------------------------------------------------------------ 21 | struct AnalyseProgressData 22 | { 23 | enum class State 24 | { 25 | BeginAnalyse, 26 | PerformAnalyse, 27 | EndAnalyse, 28 | }; 29 | 30 | Id audio_source_id{0}; 31 | State state{State::BeginAnalyse}; 32 | }; 33 | 34 | //------------------------------------------------------------------------ 35 | // AudioSource 36 | //------------------------------------------------------------------------ 37 | class AudioSource : public ARA::PlugIn::AudioSource 38 | { 39 | public: 40 | //-------------------------------------------------------------------- 41 | using SampleType = float; 42 | using MultiChannelBufferType = 43 | mam::audio_buffer_management::MultiChannelBuffers; 44 | using MetaWords = mam::meta_words::MetaWords; 45 | using FnChanged = std::function; 46 | using FuncAnalyseProgress = std::function; 47 | 48 | AudioSource(ARA::PlugIn::Document* document, 49 | ARA::ARAAudioSourceHostRef hostRef, 50 | Id id); 51 | ~AudioSource() override; 52 | 53 | auto updateRenderSampleCache() -> void; 54 | auto destroyRenderSampleCache() -> void; 55 | auto getRenderSampleCache(ARA::ARAChannelCount channel) const -> const 56 | float*; 57 | auto get_audio_buffers() -> MultiChannelBufferType&; 58 | auto get_meta_words() const -> const MetaWords&; 59 | auto set_meta_words(const MetaWords& meta_words) -> void; 60 | auto get_id() const -> Id { return id; } 61 | 62 | FuncAnalyseProgress analyse_progress_func; 63 | 64 | //-------------------------------------------------------------------- 65 | protected: 66 | void begin_analysis(); 67 | void perform_analysis(); 68 | void end_analysis(); 69 | 70 | Id id{0}; 71 | OptionalId task_id; 72 | MultiChannelBufferType audio_buffers; 73 | MetaWords meta_words; 74 | }; 75 | 76 | //------------------------------------------------------------------------ 77 | } // namespace mam::meta_words 78 | -------------------------------------------------------------------------------- /source/meta_words_editor_renderer.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "meta_words_editor_renderer.h" 6 | 7 | namespace mam::meta_words { 8 | 9 | //------------------------------------------------------------------------ 10 | EditorRenderer::EditorRenderer( 11 | ARA::PlugIn::DocumentController* document_controller) noexcept 12 | : ARA::PlugIn::EditorRenderer(document_controller) 13 | { 14 | } 15 | 16 | //------------------------------------------------------------------------ 17 | auto EditorRenderer::update_project_time(Seconds time_) -> void 18 | { 19 | this->time = time_; 20 | } 21 | 22 | //------------------------------------------------------------------------ 23 | } // namespace mam::meta_words -------------------------------------------------------------------------------- /source/meta_words_editor_renderer.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "warn_cpp/suppress_warnings.h" 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "ARA_Library/PlugIn/ARAPlug.h" 10 | END_SUPPRESS_WARNINGS 11 | 12 | namespace mam::meta_words { 13 | 14 | //------------------------------------------------------------------------ 15 | class EditorRenderer : public ARA::PlugIn::EditorRenderer 16 | { 17 | public: 18 | //-------------------------------------------------------------------- 19 | using Seconds = double; 20 | 21 | explicit EditorRenderer( 22 | ARA::PlugIn::DocumentController* document_controller) noexcept; 23 | 24 | auto update_project_time(Seconds time) -> void; 25 | //-------------------------------------------------------------------- 26 | private: 27 | Seconds time = 0.; 28 | }; 29 | 30 | //------------------------------------------------------------------------ 31 | } // namespace mam::meta_words -------------------------------------------------------------------------------- /source/meta_words_editor_view.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "meta_words_editor_view.h" 6 | #include "ara_document_controller.h" 7 | #include "meta_words_playback_region.h" 8 | 9 | namespace mam::meta_words { 10 | 11 | //------------------------------------------------------------------------ 12 | EditorView::EditorView( 13 | ARA::PlugIn::DocumentController* document_controller) noexcept 14 | : ARA::PlugIn::EditorView(document_controller) 15 | { 16 | } 17 | 18 | //-------------------------------------------------------------------- 19 | void EditorView::doNotifySelection( 20 | const ARA::PlugIn::ViewSelection* selection) noexcept 21 | { 22 | const auto& playback_regions = 23 | selection->getPlaybackRegions(); 24 | if (playback_regions.empty()) 25 | return; 26 | 27 | const auto& first_region = playback_regions.front(); 28 | const auto region_id = first_region->get_id(); 29 | auto* controller = getDocumentController(); 30 | if (controller) 31 | controller->on_region_selected_by_host(region_id); 32 | } 33 | 34 | //------------------------------------------------------------------------ 35 | } // namespace mam::meta_words -------------------------------------------------------------------------------- /source/meta_words_editor_view.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "warn_cpp/suppress_warnings.h" 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "ARA_Library/PlugIn/ARAPlug.h" 10 | END_SUPPRESS_WARNINGS 11 | 12 | namespace mam::meta_words { 13 | 14 | //------------------------------------------------------------------------ 15 | class EditorView : public ARA::PlugIn::EditorView 16 | { 17 | public: 18 | //-------------------------------------------------------------------- 19 | explicit EditorView( 20 | ARA::PlugIn::DocumentController* document_controller) noexcept; 21 | 22 | //-------------------------------------------------------------------- 23 | private: 24 | void doNotifySelection( 25 | const ARA::PlugIn::ViewSelection* selection) noexcept override; 26 | }; 27 | 28 | //------------------------------------------------------------------------ 29 | } // namespace mam::meta_words 30 | -------------------------------------------------------------------------------- /source/meta_words_playback_region.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "meta_words_playback_region.h" 4 | #include "meta_words_audio_source.h" 5 | #include "warn_cpp/suppress_warnings.h" 6 | #include 7 | BEGIN_SUPPRESS_WARNINGS 8 | #include "ARA_Library/PlugIn/ARAPlug.h" 9 | END_SUPPRESS_WARNINGS 10 | 11 | namespace mam::meta_words { 12 | 13 | //------------------------------------------------------------------------ 14 | using Word = StringType; 15 | using PunctuationMarks = std::array; 16 | static const PunctuationMarks PUNCTUATION_MARKS = {".", "?", "!", ",", ";", 17 | "-", "(", ")", "[", "]", 18 | "{", "}", "'", "\"", "..."}; 19 | static auto is_puntuation_mark(const Word& word) -> bool 20 | { 21 | for (auto& el : PUNCTUATION_MARKS) 22 | { 23 | if (el == word) 24 | return true; 25 | } 26 | return false; 27 | } 28 | 29 | //------------------------------------------------------------------------ 30 | using Seconds = const RegionData::Seconds; 31 | 32 | static auto calculate_project_offset(const PlaybackRegion& region) -> Seconds 33 | { 34 | return region.getStartInPlaybackTime() - 35 | region.getStartInAudioModificationTime(); 36 | } 37 | 38 | //------------------------------------------------------------------------ 39 | static auto is_in_playback_region(const PlaybackRegion& region, 40 | const meta_words::MetaWord& word) -> bool 41 | { 42 | const auto start_time = region.getStartInAudioModificationTime(); 43 | const auto end_time = (region.getStartInAudioModificationTime() + 44 | region.getDurationInAudioModificationTime()); 45 | 46 | const auto word_end = word.begin + word.duration; 47 | 48 | const bool is_in = (word.begin >= start_time && word.begin < end_time) || 49 | (word_end >= start_time && word_end < end_time); 50 | 51 | return is_in; 52 | } 53 | 54 | //------------------------------------------------------------------------ 55 | static auto 56 | mark_clipped_words(RegionWordDataset& words, 57 | const PlaybackRegion& region) -> RegionWordDataset 58 | { 59 | for (auto& word_data : words) 60 | { 61 | word_data.is_clipped_by_region = 62 | !is_in_playback_region(region, word_data.word); 63 | } 64 | 65 | return words; 66 | } 67 | 68 | //------------------------------------------------------------------------ 69 | static auto 70 | collect_meta_words(const PlaybackRegion& region) -> const RegionWordDataset 71 | { 72 | RegionWordDataset word_dataset; 73 | 74 | if (const auto* modification = region.getAudioModification()) 75 | { 76 | if (const auto* source = modification->getAudioSource()) 77 | { 78 | const auto meta_words = source->get_meta_words(); 79 | for (const auto& meta_word : meta_words) 80 | { 81 | RegionWordData word_data; 82 | word_data.word = meta_word; 83 | word_data.is_clipped_by_region = true; 84 | word_data.is_punctuation_mark = 85 | is_puntuation_mark(meta_word.value); 86 | word_dataset.push_back(word_data); 87 | } 88 | } 89 | } 90 | 91 | return word_dataset; 92 | } 93 | 94 | //------------------------------------------------------------------------ 95 | Id PlaybackRegion::new_id = PlaybackRegion::INVALID_ID; 96 | PlaybackRegion::PlaybackRegion( 97 | ARA::PlugIn::AudioModification* audioModification, 98 | ARA::ARAPlaybackRegionHostRef hostRef) noexcept 99 | : ARA::PlugIn::PlaybackRegion(audioModification, hostRef) 100 | , id(++new_id) 101 | { 102 | } 103 | 104 | //------------------------------------------------------------------------ 105 | auto PlaybackRegion::get_effective_color() const -> Color 106 | { 107 | Color color = std::make_tuple(0.f, 0.f, 0.f); 108 | if (const auto& tmp_color = getEffectiveColor()) 109 | { 110 | color = std::make_tuple(tmp_color->r, tmp_color->g, tmp_color->b); 111 | } 112 | 113 | return color; 114 | } 115 | //------------------------------------------------------------------------ 116 | const RegionData PlaybackRegion::get_region_data() const 117 | { 118 | RegionData data; 119 | 120 | data.words = collect_meta_words(*this); 121 | // Since we calculate everything in seconds, we dont need modify timestamps 122 | // to the sample rate 123 | // data.words = modify_time_stamps(data.words, *this, playback_sample_rate); 124 | data.words = mark_clipped_words(data.words, *this); 125 | data.project_offset = calculate_project_offset(*this); 126 | data.project_time_start = getStartInPlaybackTime(); 127 | data.duration = getDurationInPlaybackTime(); 128 | 129 | if (getRegionSequence()) 130 | { 131 | data.name = getEffectiveName(); 132 | data.color = get_effective_color(); 133 | } 134 | 135 | return data; 136 | } 137 | 138 | //------------------------------------------------------------------------ 139 | auto PlaybackRegion::get_audio_buffer() const -> const AudioBufferSpanData 140 | { 141 | const auto& audioSrc = this->getAudioModification() 142 | ->getAudioSource(); 143 | 144 | const auto& audio_buffers = audioSrc->get_audio_buffers(); 145 | const auto& left_channel = audio_buffers.at(0); 146 | AudioBufferSpan buffer_span{left_channel}; 147 | 148 | const auto start_samples = size_t(this->getStartInAudioModificationTime() * 149 | audioSrc->getSampleRate()); 150 | auto duration_samples = 151 | size_t(this->getDurationInPlaybackTime() * audioSrc->getSampleRate()); 152 | 153 | duration_samples = std::min(duration_samples, left_channel.size()); 154 | 155 | const AudioBufferSpanData data{ 156 | start_samples, 157 | AudioBufferSpan(left_channel).subspan(start_samples, duration_samples)}; 158 | 159 | return data; 160 | } 161 | //------------------------------------------------------------------------ 162 | } // namespace mam::meta_words 163 | -------------------------------------------------------------------------------- /source/meta_words_playback_region.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "meta_words_audio_source.h" 6 | #include "nonstd.h" 7 | #include "region_data.h" 8 | #include "warn_cpp/suppress_warnings.h" 9 | #include "wordify_types.h" 10 | #include 11 | BEGIN_SUPPRESS_WARNINGS 12 | #include "ARA_Library/PlugIn/ARAPlug.h" 13 | END_SUPPRESS_WARNINGS 14 | 15 | namespace mam::meta_words { 16 | 17 | //------------------------------------------------------------------------ 18 | class PlaybackRegion : public ARA::PlugIn::PlaybackRegion 19 | { 20 | public: 21 | //-------------------------------------------------------------------- 22 | static constexpr Id INVALID_ID = 0; 23 | using Color = RegionData::Color; 24 | 25 | using AudioBuf = 26 | mam::audio_buffer_management::AudioBuffer; 27 | using AudioBufferSpan = nonstd::span; 28 | 29 | struct AudioBufferSpanData 30 | { 31 | size_t offset_samples = 0; 32 | AudioBufferSpan audio_buffer_span; 33 | }; 34 | 35 | explicit PlaybackRegion(ARA::PlugIn::AudioModification* audioModification, 36 | ARA::ARAPlaybackRegionHostRef hostRef) noexcept; 37 | 38 | auto get_region_data() const -> const RegionData; 39 | auto get_audio_buffer() const -> const AudioBufferSpanData; 40 | auto get_id() const -> Id { return id; } 41 | auto get_effective_color() const -> Color; 42 | 43 | //-------------------------------------------------------------------- 44 | private: 45 | static Id new_id; 46 | Id id = INVALID_ID; 47 | }; 48 | 49 | //------------------------------------------------------------------------ 50 | using OptPlaybackRegionPtr = std::optional; 51 | 52 | //------------------------------------------------------------------------ 53 | } // namespace mam::meta_words 54 | -------------------------------------------------------------------------------- /source/meta_words_playback_renderer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "meta_words_playback_renderer.h" 4 | #include "ara_document_controller.h" 5 | #include "meta_words_audio_source.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | #include 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "ARA_Library/Utilities/ARASamplePositionConversion.h" 10 | END_SUPPRESS_WARNINGS 11 | 12 | namespace mam::meta_words { 13 | 14 | //------------------------------------------------------------------------ 15 | // PlaybackRenderer 16 | //------------------------------------------------------------------------ 17 | void PlaybackRenderer::renderPlaybackRegions( 18 | float* const* ppOutput, 19 | ARA::ARASamplePosition samplePosition, 20 | ARA::ARASampleCount samplesToRender, 21 | bool isPlayingBack) 22 | { 23 | // initialize output buffers with silence, in case no viable playback 24 | // region intersects with the current buffer, or if the model is 25 | // currently not accessible due to being edited. 26 | for (auto c{0}; c < _channelCount; ++c) 27 | std::memset(ppOutput[c], 0, 28 | sizeof(float) * static_cast(samplesToRender)); 29 | 30 | // only output samples while host is playing back 31 | if (!isPlayingBack) 32 | return; 33 | 34 | // flag that we've started rendering to prevent the document from being 35 | // edited while in this callback see TestDocumentController for details. 36 | auto docController{getDocumentController()}; 37 | if (docController->rendererWillAccessModelGraph(this)) 38 | { 39 | const auto sampleEnd{samplePosition + samplesToRender}; 40 | for (const auto& playbackRegion : getPlaybackRegions()) 41 | { 42 | const auto audioSource{playbackRegion->getAudioModification() 43 | ->getAudioSource()}; 44 | 45 | // render silence if access is currently disabled 46 | // (this is done here only to ease host debugging - actual 47 | // plug-ins would have at least some samples cached for realtime 48 | // access and would continue unless there's a cache miss.) 49 | if (!audioSource->isSampleAccessEnabled()) 50 | continue; 51 | 52 | // this simplified test code "rendering" only produces audio if 53 | // the sample rate matches 54 | // if (audioSource->getSampleRate() != _sampleRate) 55 | // continue; 56 | 57 | // evaluate region borders in song time, calculate sample range 58 | // to copy in song time (if a plug-in uses playback region 59 | // head/tail time, it will also need to reflect these values 60 | // here) 61 | const auto regionStartSample = 62 | playbackRegion->getStartInPlaybackSamples(_sampleRate); 63 | if (sampleEnd <= regionStartSample) 64 | continue; 65 | 66 | const auto regionEndSample{ 67 | playbackRegion->getEndInPlaybackSamples(_sampleRate)}; 68 | if (regionEndSample <= samplePosition) 69 | continue; 70 | 71 | auto startSongSample = std::max(regionStartSample, samplePosition); 72 | auto endSongSample = std::min(regionEndSample, sampleEnd); 73 | 74 | // calculate offset between song and audio source samples, clip 75 | // at region borders in audio source samples (if a plug-in 76 | // supports time stretching, it will also need to reflect the 77 | // stretch factor here) 78 | // const auto offsetToPlaybackRegion = 79 | // playbackRegion->getStartInAudioModificationSamples() - 80 | // regionStartSample; 81 | const auto offsetToPlaybackRegion = 82 | ARA::samplePositionAtTime( 83 | playbackRegion->getStartInAudioModificationTime(), 84 | _sampleRate) - 85 | regionStartSample; 86 | 87 | const auto startAvailableSourceSamples = 88 | std::max(ARA::ARASamplePosition{0}, 89 | ARA::samplePositionAtTime( 90 | playbackRegion->getStartInAudioModificationTime(), 91 | _sampleRate)); 92 | 93 | const auto endAvailableSourceSamples = 94 | std::min(audioSource->getSampleCount(), 95 | ARA::samplePositionAtTime( 96 | playbackRegion->getEndInAudioModificationTime(), 97 | _sampleRate)); 98 | 99 | startSongSample = 100 | std::max(startSongSample, 101 | startAvailableSourceSamples - offsetToPlaybackRegion); 102 | endSongSample = std::min(endSongSample, endAvailableSourceSamples - 103 | offsetToPlaybackRegion); 104 | if (endSongSample <= startSongSample) 105 | continue; 106 | 107 | // add samples from audio source 108 | const auto sourceChannelCount{audioSource->getChannelCount()}; 109 | for (auto posInSong{startSongSample}; posInSong < endSongSample; 110 | ++posInSong) 111 | { 112 | const auto posInBuffer{posInSong - samplePosition}; 113 | const auto posInSource{posInSong + offsetToPlaybackRegion}; 114 | if (sourceChannelCount == _channelCount) 115 | { 116 | for (auto c{0}; c < sourceChannelCount; ++c) 117 | { 118 | const auto* channel_samples = 119 | audioSource->getRenderSampleCache(c); 120 | ppOutput[c][posInBuffer] += 121 | channel_samples[posInSource]; 122 | } 123 | } 124 | else 125 | { 126 | // crude channel format conversion: 127 | // mix down to mono, then distribute the mono signal 128 | // evenly to all channels. note that when down-mixing to 129 | // mono, the result is scaled by channel count, whereas 130 | // upon up-mixing it is just copied to all channels. 131 | // \todo ambisonic formats should just stick with the 132 | // mono sum on channel 0, 133 | // but in this simple test code we currently do 134 | // not distinguish ambisonics 135 | float monoSum{0.0f}; 136 | for (auto c{0}; c < sourceChannelCount; ++c) 137 | monoSum += 138 | audioSource->getRenderSampleCache(c)[posInSource]; 139 | if (sourceChannelCount > 1) 140 | monoSum /= static_cast(sourceChannelCount); 141 | for (auto c{0}; c < _channelCount; ++c) 142 | ppOutput[c][posInBuffer] = monoSum; 143 | } 144 | } 145 | } 146 | 147 | // let the document controller know we're done 148 | docController->rendererDidAccessModelGraph(this); 149 | } 150 | } 151 | 152 | //------------------------------------------------------------------------ 153 | void PlaybackRenderer::enableRendering( 154 | ARA::ARASampleRate sampleRate, 155 | ARA::ARAChannelCount channelCount, 156 | ARA::ARASampleCount maxSamplesToRender) noexcept 157 | { 158 | // proper plug-ins would use this call to manage the resources which 159 | // they need for rendering, but our test plug-in caches everything it 160 | // needs in-memory anyways, so this method is near-empty 161 | _sampleRate = sampleRate; 162 | _channelCount = channelCount; 163 | _maxSamplesToRender = maxSamplesToRender; 164 | #if ARA_VALIDATE_API_CALLS 165 | _isRenderingEnabled = true; 166 | #endif 167 | } 168 | 169 | //------------------------------------------------------------------------ 170 | void PlaybackRenderer::disableRendering() noexcept 171 | { 172 | #if ARA_VALIDATE_API_CALLS 173 | _isRenderingEnabled = false; 174 | #endif 175 | } 176 | 177 | //------------------------------------------------------------------------ 178 | #if ARA_VALIDATE_API_CALLS 179 | void PlaybackRenderer::willAddPlaybackRegion( 180 | ARA::PlugIn::PlaybackRegion* /*playbackRegion*/) noexcept 181 | { 182 | ARA_VALIDATE_API_STATE(!_isRenderingEnabled); 183 | } 184 | 185 | //------------------------------------------------------------------------ 186 | void PlaybackRenderer::willRemovePlaybackRegion( 187 | ARA::PlugIn::PlaybackRegion* /*playbackRegion*/) noexcept 188 | { 189 | ARA_VALIDATE_API_STATE(!_isRenderingEnabled); 190 | } 191 | #endif 192 | 193 | //------------------------------------------------------------------------ 194 | 195 | } // namespace mam::meta_words 196 | -------------------------------------------------------------------------------- /source/meta_words_playback_renderer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "warn_cpp/suppress_warnings.h" 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "ARA_Library/PlugIn/ARAPlug.h" 8 | END_SUPPRESS_WARNINGS 9 | 10 | namespace mam::meta_words { 11 | 12 | //------------------------------------------------------------------------ 13 | // ARAPlaybackRenderer 14 | //------------------------------------------------------------------------ 15 | class PlaybackRenderer : public ARA::PlugIn::PlaybackRenderer 16 | { 17 | public: 18 | //------------------------------------------------------------------------ 19 | using ARA::PlugIn::PlaybackRenderer::PlaybackRenderer; 20 | 21 | void renderPlaybackRegions(float* const* ppOutput, 22 | ARA::ARASamplePosition samplePosition, 23 | ARA::ARASampleCount samplesToRender, 24 | bool isPlayingBack); 25 | 26 | void enableRendering(ARA::ARASampleRate sampleRate, 27 | ARA::ARAChannelCount channelCount, 28 | ARA::ARASampleCount maxSamplesToRender) noexcept; 29 | void disableRendering() noexcept; 30 | 31 | protected: 32 | //------------------------------------------------------------------------ 33 | #if ARA_VALIDATE_API_CALLS 34 | void willAddPlaybackRegion( 35 | ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; 36 | void willRemovePlaybackRegion( 37 | ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; 38 | #endif 39 | 40 | private: 41 | ARA::ARASampleRate _sampleRate{44100.0f}; 42 | ARA::ARASampleCount _maxSamplesToRender{4096}; 43 | ARA::ARAChannelCount _channelCount{1}; 44 | #if ARA_VALIDATE_API_CALLS 45 | bool _isRenderingEnabled{false}; 46 | #endif 47 | }; 48 | 49 | //------------------------------------------------------------------------ 50 | } // namespace mam::meta_words 51 | -------------------------------------------------------------------------------- /source/meta_words_serde.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "meta_words_serde.h" 6 | #include "nlohmann/json.hpp" 7 | 8 | namespace mam::meta_words { 9 | 10 | //------------------------------------------------------------------------ 11 | using JsonString = StringType; 12 | using json = nlohmann::json; 13 | void to_json(json& j, const MetaWord& m) 14 | { 15 | j = json{{"word", m.value}, {"begin", m.begin}, {"duration", m.duration}}; 16 | } 17 | 18 | //------------------------------------------------------------------------ 19 | void from_json(const json& j, MetaWord& m) 20 | { 21 | j.at("word").get_to(m.value); 22 | j.at("begin").get_to(m.begin); 23 | j.at("duration").get_to(m.duration); 24 | } 25 | 26 | //------------------------------------------------------------------------ 27 | 28 | namespace serde { 29 | 30 | //------------------------------------------------------------------------ 31 | void to_json(json& j, const AudioSource& mws) 32 | { 33 | j = json{{"persistent_id", mws.persistent_id}, {"words", mws.words}}; 34 | } 35 | 36 | //------------------------------------------------------------------------ 37 | void from_json(const json& j, AudioSource& mws) 38 | { 39 | j.at("persistent_id").get_to(mws.persistent_id); 40 | j.at("words").get_to(mws.words); 41 | } 42 | 43 | //------------------------------------------------------------------------ 44 | void to_json(json& j, const Archive& archive) 45 | { 46 | j = json{{"archive_version", archive.version}, 47 | {"audio_sources", archive.audio_sources}}; 48 | } 49 | 50 | //------------------------------------------------------------------------ 51 | void from_json(const json& j, Archive& archive) 52 | { 53 | j.at("archive_version").get_to(archive.version); 54 | j.at("audio_sources").get_to(archive.audio_sources); 55 | } 56 | 57 | //------------------------------------------------------------------------ 58 | auto serialize(const Archive& archive, JsonString& s) -> bool 59 | { 60 | json j = archive; 61 | s = j.dump(); 62 | 63 | return true; 64 | } 65 | //------------------------------------------------------------------------ 66 | auto deserialize(const JsonString& s, Archive& archive) -> bool 67 | { 68 | if (s.empty()) 69 | return false; 70 | 71 | json j = json::parse(s); 72 | archive = j.template get(); 73 | 74 | return true; 75 | } 76 | //------------------------------------------------------------------------ 77 | } // namespace serde 78 | } // namespace mam::meta_words 79 | -------------------------------------------------------------------------------- /source/meta_words_serde.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "mam/meta_words/meta_word.h" 8 | #include "wordify_types.h" 9 | 10 | namespace mam::meta_words::serde { 11 | //------------------------------------------------------------------------ 12 | using PersistentId = StringType; 13 | 14 | struct AudioSource 15 | { 16 | PersistentId persistent_id; 17 | MetaWords words; 18 | }; 19 | 20 | struct Archive 21 | { 22 | using AudioSources = std::vector; 23 | 24 | size_t version = 1; 25 | AudioSources audio_sources; 26 | }; 27 | 28 | //------------------------------------------------------------------------ 29 | auto serialize(const Archive& archive, StringType& s) -> bool; 30 | auto deserialize(const StringType& s, Archive& archive) -> bool; 31 | 32 | //------------------------------------------------------------------------ 33 | } // namespace mam::meta_words::serde 34 | -------------------------------------------------------------------------------- /source/nonstd.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #if __cplusplus >= 202002L 6 | #include 7 | #else 8 | #include "gsl/span" 9 | #endif 10 | 11 | namespace nonstd { 12 | 13 | //------------------------------------------------------------------------ 14 | #if __cplusplus >= 202002L 15 | template 16 | using span = std::span; 17 | #else 18 | template 19 | using span = gsl::span; 20 | #endif 21 | 22 | //------------------------------------------------------------------------ 23 | } // namespace nonstd -------------------------------------------------------------------------------- /source/parameter_ids.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "pluginterfaces/base/funknown.h" 8 | #include "pluginterfaces/vst/vsttypes.h" 9 | 10 | namespace mam { 11 | //------------------------------------------------------------------------ 12 | enum ParamIds 13 | { 14 | kParamIdColorScheme = 0, 15 | kParamIdAnalyzeTaskCount, 16 | kParamIdSmartSearchMode, 17 | kParamIdSmartSearchNext, 18 | kParamIdSmartSearchPrev, 19 | }; 20 | 21 | //------------------------------------------------------------------------ 22 | } // namespace mam 23 | -------------------------------------------------------------------------------- /source/preferences_serde.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "preferences_serde.h" 6 | #include "hao/special_folders/special_folders.h" 7 | #include "nlohmann/json.hpp" 8 | #include 9 | #include 10 | 11 | namespace mam::meta_words::serde { 12 | //------------------------------------------------------------------------ 13 | using json = nlohmann::json; 14 | static constexpr auto PREFERENCES_FILE_NAME = "preferences.json"; 15 | 16 | static constexpr auto PREFS_VERSION_KEY = "preferences_version"; 17 | static constexpr auto COLOR_SCHEME_KEY = "color_scheme"; 18 | static constexpr auto COLOR_SCHEME_LITE_VALUE = "light"; 19 | static constexpr auto COLOR_SCHEME_DARK_VALUE = "dark"; 20 | static constexpr auto SMART_SEARCH_KEY = "smart_search"; 21 | static constexpr auto SMART_SEARCH_OFF_VALUE = "off"; 22 | static constexpr auto SMART_SEARCH_ON_VALUE = "on"; 23 | //------------------------------------------------------------------------ 24 | NLOHMANN_JSON_SERIALIZE_ENUM(ColorScheme, 25 | { 26 | {Light, COLOR_SCHEME_LITE_VALUE}, 27 | {Dark, COLOR_SCHEME_DARK_VALUE}, 28 | }) 29 | 30 | NLOHMANN_JSON_SERIALIZE_ENUM(SmartSearch, 31 | { 32 | {Off, SMART_SEARCH_OFF_VALUE}, 33 | {On, SMART_SEARCH_ON_VALUE}, 34 | }) 35 | 36 | //------------------------------------------------------------------------ 37 | void to_json(json& j, const Preferences& prefs) 38 | { 39 | j = json{{PREFS_VERSION_KEY, prefs.version}, 40 | {COLOR_SCHEME_KEY, prefs.color_scheme}, 41 | {SMART_SEARCH_KEY, prefs.smart_search}}; 42 | } 43 | 44 | //------------------------------------------------------------------------ 45 | void from_json(const json& j, Preferences& prefs) 46 | { 47 | j.at(PREFS_VERSION_KEY).get_to(prefs.version); 48 | if (j.contains(COLOR_SCHEME_KEY)) 49 | j.at(COLOR_SCHEME_KEY).get_to(prefs.color_scheme); 50 | if (j.contains(SMART_SEARCH_KEY)) 51 | j.at(SMART_SEARCH_KEY).get_to(prefs.smart_search); 52 | } 53 | 54 | //------------------------------------------------------------------------ 55 | auto serialize(const Preferences& prefs, StringType& s) -> bool 56 | { 57 | json j = prefs; 58 | s = j.dump(); 59 | 60 | return true; 61 | } 62 | //------------------------------------------------------------------------ 63 | auto deserialize(const StringType& s, Preferences& prefs) -> bool 64 | { 65 | if (s.empty()) 66 | return false; 67 | 68 | json j = json::parse(s); 69 | prefs = j.template get(); 70 | 71 | return true; 72 | } 73 | 74 | //------------------------------------------------------------------------ 75 | auto write_to(const Preferences& prefs, 76 | const StringType& company_name, 77 | const StringType& plugin_name) -> bool 78 | { 79 | const auto prefs_folder = hao::special_folders::get_preferences_folder(); 80 | auto prefs_folder_path = std::filesystem::path(prefs_folder); 81 | prefs_folder_path /= company_name; 82 | prefs_folder_path /= plugin_name; 83 | 84 | if (!std::filesystem::exists(prefs_folder_path)) 85 | { 86 | if (!std::filesystem::create_directories(prefs_folder_path)) 87 | return false; 88 | } 89 | 90 | const auto prefs_file_path = prefs_folder_path / PREFERENCES_FILE_NAME; 91 | 92 | json j = prefs; 93 | std::ofstream file(prefs_file_path); 94 | file << j; 95 | 96 | return true; 97 | } 98 | 99 | //------------------------------------------------------------------------ 100 | auto read_from(const StringType& company_name, 101 | const StringType& plugin_name, 102 | Preferences& prefs) -> bool 103 | { 104 | const auto prefs_folder = hao::special_folders::get_preferences_folder(); 105 | auto prefs_folder_path = std::filesystem::path(prefs_folder); 106 | prefs_folder_path /= company_name; 107 | prefs_folder_path /= plugin_name; 108 | 109 | const auto prefs_file_path = prefs_folder_path / PREFERENCES_FILE_NAME; 110 | 111 | if (!std::filesystem::exists(prefs_file_path)) 112 | return false; 113 | 114 | std::ifstream file(prefs_file_path); 115 | prefs = json::parse(file); 116 | 117 | return true; 118 | } 119 | 120 | //------------------------------------------------------------------------ 121 | } // namespace mam::meta_words::serde 122 | -------------------------------------------------------------------------------- /source/preferences_serde.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "mam/meta_words/meta_word.h" 8 | #include "wordify_types.h" 9 | 10 | namespace mam::meta_words::serde { 11 | 12 | //------------------------------------------------------------------------ 13 | using PathType = StringType; 14 | 15 | enum ColorScheme 16 | { 17 | Light, 18 | Dark 19 | }; 20 | 21 | enum SmartSearch 22 | { 23 | Off, 24 | On 25 | }; 26 | 27 | struct Preferences 28 | { 29 | size_t version = 1; 30 | ColorScheme color_scheme{Dark}; 31 | SmartSearch smart_search{Off}; 32 | }; 33 | 34 | //------------------------------------------------------------------------ 35 | auto serialize(const Preferences& prefs, StringType& s) -> bool; 36 | auto deserialize(const StringType& s, Preferences& prefs) -> bool; 37 | 38 | auto write_to(const Preferences& prefs, 39 | const StringType& company_name, 40 | const StringType& plugin_name) -> bool; 41 | auto read_from(const StringType& company_name, 42 | const StringType& plugin_name, 43 | Preferences& prefs) -> bool; 44 | 45 | //------------------------------------------------------------------------ 46 | } // namespace mam::meta_words::serde -------------------------------------------------------------------------------- /source/region_data.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "mam/meta_words/meta_word.h" 6 | #include "wordify_types.h" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace mam { 12 | 13 | //------------------------------------------------------------------------ 14 | struct RegionWordData 15 | { 16 | bool is_clipped_by_region = true; 17 | bool is_punctuation_mark = false; 18 | meta_words::MetaWord word; 19 | }; 20 | 21 | using RegionWordDataset = std::vector; 22 | 23 | //------------------------------------------------------------------------ 24 | struct RegionData 25 | { 26 | using Seconds = double; 27 | using Color = std::tuple; 28 | 29 | StringType name; 30 | Color color; 31 | Seconds project_offset{0.}; 32 | Seconds project_time_start{0.}; 33 | Seconds duration{0.}; 34 | RegionWordDataset words; 35 | }; 36 | 37 | //------------------------------------------------------------------------ 38 | } // namespace mam 39 | -------------------------------------------------------------------------------- /source/region_order_manager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "region_order_manager.h" 4 | #include 5 | 6 | namespace mam { 7 | 8 | //------------------------------------------------------------------------ 9 | bool sort(RegionOrderManager::FuncStartInPlaybackTime& start_in_playback_time, 10 | RegionOrderManager::OrderedIds& ids) 11 | { 12 | auto sorter = [&](const Id id0, const Id id1) { 13 | const auto time0 = start_in_playback_time(id0); 14 | const auto time1 = start_in_playback_time(id1); 15 | 16 | bool result = time0 < time1; 17 | 18 | return result; 19 | }; 20 | 21 | if (std::is_sorted(ids.begin(), ids.end(), sorter)) 22 | return false; 23 | 24 | std::sort(ids.begin(), ids.end(), sorter); 25 | return true; 26 | } 27 | 28 | //------------------------------------------------------------------------ 29 | // RegionOrderManager 30 | //------------------------------------------------------------------------ 31 | auto RegionOrderManager::push_back(Id id) -> void 32 | { 33 | playback_region_ids_ordered.push_back(id); 34 | reorder(); 35 | } 36 | 37 | //------------------------------------------------------------------------ 38 | auto RegionOrderManager::remove(Id id) -> void 39 | { 40 | auto iter = std::remove(playback_region_ids_ordered.begin(), 41 | playback_region_ids_ordered.end(), id); 42 | playback_region_ids_ordered.erase(iter, playback_region_ids_ordered.end()); 43 | } 44 | 45 | //------------------------------------------------------------------------ 46 | auto RegionOrderManager::reorder() -> void 47 | { 48 | assert(start_in_playback_time_func && 49 | "start_in_playback_time_func must be set from outside!"); 50 | if (!start_in_playback_time_func) 51 | return; 52 | 53 | bool is_notify_listeners = 54 | sort(start_in_playback_time_func, playback_region_ids_ordered); 55 | 56 | if (is_notify_listeners) 57 | playback_region_order_subject({}); 58 | } 59 | 60 | //------------------------------------------------------------------------ 61 | auto RegionOrderManager::get_order_subject() -> OrderSubject* 62 | { 63 | return &playback_region_order_subject; 64 | } 65 | 66 | //------------------------------------------------------------------------ 67 | } // namespace mam 68 | -------------------------------------------------------------------------------- /source/region_order_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "meta_words_playback_region.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | BEGIN_SUPPRESS_WARNINGS 8 | #include "eventpp/callbacklist.h" 9 | END_SUPPRESS_WARNINGS 10 | 11 | namespace mam { 12 | 13 | //------------------------------------------------------------------------ 14 | struct PlaybackRegionOrderChangeData 15 | { 16 | // TODO: Nothing to do here 17 | }; 18 | 19 | //------------------------------------------------------------------------ 20 | // RegionOrderManager 21 | // 22 | // Keeps a sorted list of all playback regions IDs. The regions are always 23 | // sorted to the playback time (no matter in which sequence they are). When IDs 24 | // have been sorted, all observers will be notified. 25 | //------------------------------------------------------------------------ 26 | class RegionOrderManager 27 | { 28 | public: 29 | //-------------------------------------------------------------------- 30 | using PlaybackRegion = meta_words::PlaybackRegion; 31 | using OrderedIds = std::vector; 32 | using OrderSubject = 33 | eventpp::CallbackList; 34 | using FuncStartInPlaybackTime = std::function; 35 | 36 | auto get_order_subject() -> OrderSubject*; 37 | auto push_back(Id id) -> void; 38 | auto remove(Id id) -> void; 39 | auto reorder() -> void; 40 | 41 | template 42 | auto for_each_region_id_enumerated(Func& func) const; 43 | 44 | FuncStartInPlaybackTime start_in_playback_time_func; 45 | 46 | //-------------------------------------------------------------------- 47 | private: 48 | OrderSubject playback_region_order_subject; 49 | OrderedIds playback_region_ids_ordered; 50 | }; 51 | 52 | //------------------------------------------------------------------------ 53 | template 54 | inline auto RegionOrderManager::for_each_region_id_enumerated(Func& func) const 55 | { 56 | for (OrderedIds::size_type i = 0; i < playback_region_ids_ordered.size(); 57 | i++) 58 | { 59 | func(i, playback_region_ids_ordered.at(i)); 60 | } 61 | } 62 | 63 | //------------------------------------------------------------------------ 64 | } // namespace mam 65 | -------------------------------------------------------------------------------- /source/search_engine.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "search_engine.h" 4 | #include "meta_words_playback_region.h" 5 | #include "wordify_types.h" 6 | 7 | namespace mam { 8 | namespace { 9 | //------------------------------------------------------------------------ 10 | auto collect_search_results(const StringType& search_word, 11 | const SearchEngine::Regions& regions, 12 | SearchEngine::MatchFunc&& match_func) 13 | -> const SearchEngine::SearchResults 14 | { 15 | SearchEngine::SearchResults results; 16 | 17 | for (const auto& region : regions) 18 | { 19 | auto region_data = region.second->get_region_data(); 20 | 21 | SearchEngine::WordIndices indices; 22 | for (size_t i = 0; i < region_data.words.size(); i++) 23 | { 24 | const auto& word_data = region_data.words[i]; 25 | if (word_data.is_clipped_by_region) 26 | continue; 27 | 28 | auto word = word_data.word.value; 29 | if (match_func(word, search_word)) 30 | indices.push_back(i); 31 | } 32 | 33 | if (!indices.empty()) 34 | results.push_back({region.first, indices, std::nullopt}); 35 | } 36 | 37 | return results; 38 | } 39 | 40 | //------------------------------------------------------------------------ 41 | } // namespace 42 | 43 | //------------------------------------------------------------------------ 44 | struct SearchEngineCache 45 | { 46 | struct RegionWord 47 | { 48 | SearchEngine::RegionID region_id = 0; 49 | SearchEngine::WordIndex word_index = 0; 50 | }; 51 | 52 | static SearchEngineCache& instance() 53 | { 54 | static SearchEngineCache cache; 55 | return cache; 56 | } 57 | 58 | SearchEngine::SearchResults search_results; 59 | StringType search_word; 60 | RegionWord focused_word; 61 | }; 62 | 63 | namespace detail { 64 | //------------------------------------------------------------------------ 65 | auto search(const StringType& search_word, 66 | const SearchEngine::Regions& regions, 67 | SearchEngine::MatchFunc&& match_func) 68 | -> const SearchEngine::SearchResults& 69 | { 70 | if (SearchEngineCache::instance().search_word == search_word) 71 | return SearchEngineCache::instance().search_results; 72 | 73 | SearchEngineCache::instance().search_word = search_word; 74 | SearchEngineCache::instance().search_results = 75 | collect_search_results(search_word, regions, std::move(match_func)); 76 | 77 | // Focus the first word 78 | if (!SearchEngineCache::instance().search_results.empty()) 79 | { 80 | auto& first_result = SearchEngineCache::instance().search_results.at(0); 81 | 82 | constexpr auto DEFAULT_WORD_INDEX = 0; 83 | first_result.focused_word = DEFAULT_WORD_INDEX; 84 | SearchEngineCache::instance().focused_word = { 85 | first_result.region_id, first_result.focused_word.value()}; 86 | } 87 | 88 | return SearchEngineCache::instance().search_results; 89 | } 90 | 91 | //------------------------------------------------------------------------ 92 | auto next_occurence() -> SearchEngine::SearchResults 93 | { 94 | auto& focused_word = SearchEngineCache::instance().focused_word; 95 | auto& search_results = SearchEngineCache::instance().search_results; 96 | 97 | SearchEngine::SearchResults results; 98 | 99 | auto iter = std::find_if( 100 | search_results.begin(), search_results.end(), [&](const auto& result) { 101 | return result.region_id == focused_word.region_id; 102 | }); 103 | 104 | if (iter == search_results.end()) 105 | return results; // Should not happen, would be a serious error! 106 | 107 | focused_word.word_index++; 108 | if (focused_word.word_index < iter->indices.size()) 109 | { 110 | // Still in the same region 111 | iter->focused_word = focused_word.word_index; 112 | results.push_back(*iter); 113 | } 114 | else 115 | { 116 | // Advance from one region to the next 117 | iter->focused_word.reset(); 118 | results.push_back(*iter); 119 | 120 | iter = std::next(iter); 121 | if (iter == search_results.end()) 122 | iter = search_results.begin(); 123 | 124 | focused_word.word_index = 0; 125 | focused_word.region_id = iter->region_id; 126 | iter->focused_word = focused_word.word_index; 127 | results.push_back(*iter); 128 | } 129 | 130 | return results; 131 | } 132 | 133 | //------------------------------------------------------------------------ 134 | auto prev_occurence() -> SearchEngine::SearchResults 135 | { 136 | auto& focused_word = SearchEngineCache::instance().focused_word; 137 | auto& search_results = SearchEngineCache::instance().search_results; 138 | 139 | SearchEngine::SearchResults results; 140 | 141 | auto iter = 142 | std::find_if(search_results.rbegin(), search_results.rend(), 143 | [&](const auto& result) { 144 | return result.region_id == focused_word.region_id; 145 | }); 146 | 147 | if (iter == search_results.rend()) 148 | return results; // Should not happen, would be a serious error! 149 | 150 | if (focused_word.word_index == 0) 151 | { 152 | // Advance from one region to the next 153 | iter->focused_word.reset(); 154 | results.push_back(*iter); 155 | 156 | iter = std::next(iter); 157 | if (iter == search_results.rend()) 158 | iter = search_results.rbegin(); 159 | 160 | focused_word.word_index = iter->indices.size() - 1; 161 | focused_word.region_id = iter->region_id; 162 | iter->focused_word = focused_word.word_index; 163 | results.push_back(*iter); 164 | } 165 | else 166 | { 167 | focused_word.word_index--; 168 | 169 | // Still in the same region 170 | iter->focused_word = focused_word.word_index; 171 | results.push_back(*iter); 172 | } 173 | 174 | return results; 175 | } 176 | 177 | //------------------------------------------------------------------------ 178 | auto clear_results() -> SearchEngine::SearchResults 179 | { 180 | auto results = SearchEngineCache::instance().search_results; 181 | for (auto& result : results) 182 | { 183 | result.focused_word = std::nullopt; 184 | result.indices.clear(); 185 | } 186 | 187 | SearchEngineCache::instance().search_results.clear(); 188 | SearchEngineCache::instance().search_word.clear(); 189 | 190 | return results; 191 | } 192 | //------------------------------------------------------------------------ 193 | } // namespace detail 194 | 195 | //------------------------------------------------------------------------ 196 | // SearchEngine 197 | //------------------------------------------------------------------------ 198 | auto SearchEngine::search(const StringType& search_word, 199 | MatchFunc&& match_func) -> void 200 | { 201 | if (!get_regions) 202 | return; 203 | 204 | this->clear_results(); 205 | 206 | const auto& results = 207 | detail::search(search_word, get_regions(), std::move(match_func)); 208 | 209 | callback(results); 210 | } 211 | 212 | //------------------------------------------------------------------------ 213 | auto SearchEngine::research(MatchFunc&& match_func) -> void 214 | { 215 | const auto w = SearchEngineCache::instance().search_word; 216 | clear_results(); 217 | return search(w, std::move(match_func)); 218 | } 219 | 220 | //------------------------------------------------------------------------ 221 | auto SearchEngine::next_occurence() -> void 222 | { 223 | const auto results = mam::detail::next_occurence(); 224 | callback(results); 225 | } 226 | 227 | //------------------------------------------------------------------------ 228 | auto SearchEngine::prev_occurence() -> void 229 | { 230 | const auto results = mam::detail::prev_occurence(); 231 | callback(results); 232 | } 233 | 234 | //------------------------------------------------------------------------ 235 | auto SearchEngine::clear_results() -> void 236 | { 237 | const auto results = mam::detail::clear_results(); 238 | callback(results); 239 | } 240 | 241 | //------------------------------------------------------------------------ 242 | auto SearchEngine::current_search_word() -> StringType 243 | { 244 | return SearchEngineCache::instance().search_word; 245 | } 246 | 247 | //------------------------------------------------------------------------ 248 | auto SearchEngine::get_callback() -> SearchEngineCallback& 249 | { 250 | return callback; 251 | } 252 | 253 | //------------------------------------------------------------------------ 254 | } // namespace mam 255 | -------------------------------------------------------------------------------- /source/search_engine.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "meta_words_playback_region.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | #include "wordify_types.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | BEGIN_SUPPRESS_WARNINGS 13 | #include "eventpp/callbacklist.h" 14 | END_SUPPRESS_WARNINGS 15 | 16 | namespace mam::meta_words { 17 | class PlaybackRegion; 18 | } 19 | 20 | namespace mam { 21 | 22 | //------------------------------------------------------------------------ 23 | // SearchEngine 24 | //------------------------------------------------------------------------ 25 | class SearchEngine 26 | { 27 | public: 28 | //-------------------------------------------------------------------- 29 | using WordIndex = size_t; 30 | using WordIndices = std::vector; 31 | using RegionID = size_t; 32 | using OptWord = std::optional; 33 | using Regions = std::map; 34 | 35 | struct RegionWord 36 | { 37 | RegionID region_id = 0; 38 | WordIndex word_index = 0; 39 | }; 40 | 41 | struct SearchResult 42 | { 43 | RegionID region_id = 0; 44 | WordIndices indices; 45 | OptWord focused_word; 46 | }; 47 | 48 | using SearchResults = std::vector; 49 | using MatchFunc = 50 | std::function; 51 | using SearchEngineCallback = 52 | eventpp::CallbackList; 53 | 54 | static SearchEngine& instance() 55 | { 56 | static SearchEngine cache; 57 | return cache; 58 | } 59 | 60 | auto search(const StringType& search_word, MatchFunc&& match_func) -> void; 61 | auto research(MatchFunc&& match_func) -> void; 62 | auto next_occurence() -> void; 63 | auto prev_occurence() -> void; 64 | auto clear_results() -> void; 65 | auto current_search_word() -> StringType; 66 | auto get_callback() -> SearchEngineCallback&; 67 | 68 | using FuncRegions = std::function; 69 | FuncRegions get_regions; 70 | 71 | //-------------------------------------------------------------------- 72 | private: 73 | SearchEngineCallback callback; 74 | }; 75 | 76 | //------------------------------------------------------------------------ 77 | } // namespace mam 78 | -------------------------------------------------------------------------------- /source/string_matcher.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "string_matcher.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace mam { 11 | 12 | namespace StringMatcher { 13 | 14 | enum class FuzzyMatchStyle 15 | { 16 | none, 17 | nearbyFuzzy, 18 | intermediateFuzzy 19 | }; 20 | 21 | //------------------------------------------------------------------------ 22 | void prepareStrings(StringType& toMatch, StringType& string) 23 | { 24 | std::transform(toMatch.begin(), toMatch.end(), toMatch.begin(), ::tolower); 25 | std::transform(string.begin(), string.end(), string.begin(), ::tolower); 26 | 27 | toMatch.erase( 28 | std::remove_if( 29 | toMatch.begin(), toMatch.end(), 30 | [](char c) { return std::ispunct(static_cast(c)); }), 31 | toMatch.end()); 32 | } 33 | 34 | //------------------------------------------------------------------------ 35 | bool isDirectMatch(StringType toMatch, StringType string) 36 | { 37 | if (toMatch.length() != string.length()) 38 | return false; 39 | 40 | return (toMatch == string); 41 | } 42 | 43 | //------------------------------------------------------------------------ 44 | bool isSubMatch(StringType toMatch, StringType string) 45 | { 46 | return std::search(toMatch.begin(), toMatch.end(), string.begin(), 47 | string.end()) != toMatch.end(); 48 | } 49 | 50 | //------------------------------------------------------------------------ 51 | bool isFuzzyMatch(StringType toMatch, StringType string, FuzzyMatchStyle style) 52 | { 53 | size_t x = toMatch.length(); 54 | size_t y = string.length(); 55 | 56 | 57 | std::vector> distanceVector(x + 1, 58 | std::vector(y + 1, 0)); 59 | 60 | for (size_t i = 0; i <= x; ++i) 61 | distanceVector[i][0] = static_cast(i); 62 | for (size_t j = 0; j <= y; ++j) 63 | distanceVector[0][j] = static_cast(j); 64 | 65 | for (size_t i = 1; i <= x; ++i) 66 | { 67 | for (size_t j = 1; j <= y; ++j) 68 | { 69 | if (toMatch[i - 1] == string[j - 1]) 70 | distanceVector[i][j] = distanceVector[i - 1][j - 1]; 71 | else 72 | distanceVector[i][j] = 73 | std::min({distanceVector[i - 1][j], 74 | distanceVector[i][j - 1], 75 | distanceVector[i - 1][j - 1]}) + 76 | 1; 77 | } 78 | } 79 | 80 | int result = distanceVector[x][y]; 81 | 82 | if (style == FuzzyMatchStyle::nearbyFuzzy) 83 | return result <= 1; 84 | else if (style == FuzzyMatchStyle::intermediateFuzzy) 85 | return result <= 2; 86 | 87 | return false; 88 | } 89 | 90 | //------------------------------------------------------------------------ 91 | bool isMatch(StringType toMatch, StringType string, MatchMethod method) 92 | { 93 | prepareStrings(toMatch, string); 94 | if (method == MatchMethod::directMatch) 95 | return isDirectMatch(toMatch, string); 96 | else if (method == MatchMethod::subMatch) 97 | return isSubMatch(toMatch, string); 98 | else if (method == MatchMethod::nearbyFuzzyMatch) 99 | return isFuzzyMatch(toMatch, string, FuzzyMatchStyle::nearbyFuzzy); 100 | else if (method == MatchMethod::intermediateFuzzyMatch) 101 | return isFuzzyMatch(toMatch, string, 102 | FuzzyMatchStyle::intermediateFuzzy); 103 | 104 | return false; 105 | } 106 | 107 | } // namespace StringMatcher 108 | } // namespace mam 109 | -------------------------------------------------------------------------------- /source/string_matcher.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "wordify_types.h" 8 | 9 | namespace mam { 10 | 11 | //------------------------------------------------------------------------ 12 | // StringMatcher 13 | //------------------------------------------------------------------------ 14 | namespace StringMatcher { 15 | enum class MatchMethod 16 | { 17 | directMatch, 18 | subMatch, 19 | nearbyFuzzyMatch, 20 | intermediateFuzzyMatch 21 | 22 | }; 23 | 24 | bool isMatch(StringType toMatch, StringType string, MatchMethod method); 25 | 26 | }; // namespace StringMatcher 27 | 28 | //------------------------------------------------------------------------ 29 | } // namespace mam 30 | -------------------------------------------------------------------------------- /source/task_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "eventpp/callbacklist.h" 6 | #include "mam/meta_words/runner.h" 7 | #include "wordify_types.h" 8 | #include 9 | 10 | namespace mam::task_managing { 11 | 12 | //------------------------------------------------------------------------ 13 | using ResultData = meta_words::ExpectedMetaWords; 14 | struct Expected 15 | { 16 | bool was_canceled = false; 17 | ResultData data; 18 | }; 19 | 20 | using PathType = StringType; 21 | using InputData = PathType; 22 | using FuncFinished = std::function; 23 | using TaskCountCallback = eventpp::CallbackList; 24 | 25 | auto append_task(const InputData& input_data, FuncFinished&& finished_callback) 26 | -> Id; 27 | auto cancel_task(Id task_id) -> bool; 28 | auto count_tasks() -> size_t; 29 | auto get_task_count_callback() -> TaskCountCallback*; 30 | 31 | //------------------------------------------------------------------------ 32 | } // namespace mam::task_managing 33 | -------------------------------------------------------------------------------- /source/tiny_selection_model.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | namespace mam { 8 | 9 | //------------------------------------------------------------------------ 10 | // SelectionModel 11 | //------------------------------------------------------------------------ 12 | template 13 | class SelectionModel 14 | { 15 | public: 16 | //------------------------------------------------------------------------ 17 | using DataType = T; 18 | using FuncOnSelect = std::function; 19 | 20 | void select(const DataType& data) 21 | { 22 | this->selection_data = data; 23 | if (on_select_func) 24 | on_select_func(this->selection_data); 25 | } 26 | 27 | auto get_data() const -> const DataType& { return selection_data; } 28 | 29 | FuncOnSelect on_select_func; 30 | 31 | //------------------------------------------------------------------------ 32 | private: 33 | DataType selection_data{0}; 34 | }; 35 | 36 | //------------------------------------------------------------------------ 37 | 38 | } // namespace mam 39 | -------------------------------------------------------------------------------- /source/version.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "pluginterfaces/base/fplatform.h" 8 | 9 | // Plain project version file generated by cmake 10 | #include "projectversion.h" 11 | 12 | #define stringOriginalFilename PROJECT_NAME ".vst3" 13 | #if SMTG_PLATFORM_64 14 | #define stringFileDescription PROJECT_NAME " VST3 (64Bit)" 15 | #else 16 | #define stringFileDescription PROJECT_NAME " VST3" 17 | #endif 18 | #define stringCompanyName CPACK_PACKAGE_VENDOR "\0" 19 | #define stringLegalCopyright PROJECT_LEGAL_COPYRIGHT 20 | #define stringLegalTrademarks \ 21 | "VST is a trademark of Steinberg Media Technologies GmbH" 22 | -------------------------------------------------------------------------------- /source/views/hstack_layout.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "hstack_layout.h" 4 | #include "warn_cpp/suppress_warnings.h" 5 | #include 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "vstgui/lib/controls/ccontrol.h" 8 | END_SUPPRESS_WARNINGS 9 | 10 | namespace mam { 11 | 12 | //------------------------------------------------------------------------ 13 | using CCoord = VSTGUI::CCoord; 14 | using CRect = VSTGUI::CRect; 15 | using CPoint = VSTGUI::CPoint; 16 | using CControl = VSTGUI::CControl; 17 | using CRects = std::vector; 18 | 19 | //------------------------------------------------------------------------ 20 | static auto 21 | layout_row_stack(const CPoint parent, 22 | CRects& rects, 23 | const HStackLayout::Spacing& spacing, 24 | const HStackLayout::Padding& padding) -> const CCoord 25 | { 26 | if (rects.empty()) 27 | return {}; 28 | 29 | CPoint offset(padding.left, padding.top); 30 | const CPoint origin(padding.left, padding.top); 31 | 32 | const auto default_height = rects.begin()->getHeight(); 33 | 34 | for (auto& rect : rects) 35 | { 36 | rect.setHeight(default_height); 37 | rect.moveTo(offset); 38 | 39 | // The first rect never gets a word wrap, even is parent rect is very 40 | // narrow 41 | const bool is_first_rect = offset == origin; 42 | const bool is_line_end = 43 | (offset.x + rect.getWidth() + padding.right) > parent.x; 44 | const bool needs_word_wrap = is_line_end && !is_first_rect; 45 | if (needs_word_wrap) 46 | { 47 | offset.x = padding.left; // Carriage Return 48 | offset.y += default_height + spacing.verti; // Line Feed 49 | rect.moveTo(offset); 50 | } 51 | 52 | offset.x += rect.getWidth() + spacing.horiz; // Next word horiz pos 53 | } 54 | 55 | return offset.y + default_height + padding.bottom; 56 | } 57 | 58 | //------------------------------------------------------------------------ 59 | static auto collect_view_size(HStackLayout::ViewContainer* container) -> CRects 60 | { 61 | CRects rects; 62 | container->forEachChild([&](HStackLayout::View* child) { 63 | if (child->isSubview()) 64 | rects.push_back(child->getViewSize()); 65 | }); 66 | 67 | return rects; 68 | } 69 | 70 | //------------------------------------------------------------------------ 71 | static auto apply_view_size(HStackLayout::ViewContainer* container, 72 | const CRects& rects) -> void 73 | { 74 | size_t count = 0; 75 | container->forEachChild([&](HStackLayout::View* child) { 76 | if (!child->isSubview()) 77 | return; 78 | 79 | const auto rect = child->getViewSize(); 80 | const auto new_rect = rects[count++]; 81 | if (rect != new_rect) 82 | child->setViewSize(new_rect); 83 | }); 84 | } 85 | 86 | //------------------------------------------------------------------------ 87 | static auto do_layout(HStackLayout::ViewContainer* container, 88 | const HStackLayout::Spacing& spacing, 89 | const HStackLayout::Padding& padding) -> void 90 | { 91 | // Layout children 92 | auto parent_size = container->getViewSize(); 93 | CRects sizes = collect_view_size(container); 94 | const auto new_height = 95 | layout_row_stack({parent_size.getWidth(), parent_size.getHeight()}, 96 | sizes, spacing, padding); 97 | 98 | // Adjust parent container height 99 | parent_size.setHeight(new_height); 100 | container->setViewSize(parent_size); 101 | apply_view_size(container, sizes); 102 | } 103 | 104 | //------------------------------------------------------------------------ 105 | // HStackLayout 106 | //------------------------------------------------------------------------ 107 | HStackLayout::HStackLayout(ViewContainer* container) 108 | : observed_container(container) 109 | { 110 | if (container) 111 | { 112 | container->registerViewListener(this); 113 | container->registerViewContainerListener(this); 114 | } 115 | } 116 | 117 | //------------------------------------------------------------------------ 118 | HStackLayout::~HStackLayout() 119 | { 120 | if (observed_container) 121 | { 122 | observed_container->unregisterViewContainerListener(this); 123 | observed_container->unregisterViewListener(this); 124 | } 125 | } 126 | 127 | //------------------------------------------------------------------------ 128 | void HStackLayout::viewWillDelete(View* view) 129 | { 130 | if (view == observed_container) 131 | { 132 | observed_container->unregisterViewContainerListener(this); 133 | observed_container->unregisterViewListener(this); 134 | observed_container = nullptr; 135 | } 136 | } 137 | 138 | //------------------------------------------------------------------------ 139 | void HStackLayout::viewContainerViewAdded(ViewContainer* container, 140 | View* /*view*/) 141 | { 142 | if (observed_container == container) 143 | do_layout(observed_container, spacing, padding); 144 | } 145 | 146 | //------------------------------------------------------------------------ 147 | void HStackLayout::viewContainerViewRemoved(ViewContainer* container, 148 | View* /*view*/) 149 | { 150 | if (observed_container == container) 151 | do_layout(observed_container, spacing, padding); 152 | } 153 | 154 | //------------------------------------------------------------------------ 155 | void HStackLayout::viewSizeChanged(View* /*view*/, const Rect& /*oldSize*/) 156 | { 157 | do_layout(observed_container, spacing, padding); 158 | } 159 | 160 | //------------------------------------------------------------------------ 161 | void HStackLayout::setup(const Spacing& spacing_value, 162 | const Padding& padding_value) 163 | { 164 | this->spacing = spacing_value; 165 | this->padding = padding_value; 166 | } 167 | 168 | //------------------------------------------------------------------------ 169 | 170 | } // namespace mam 171 | -------------------------------------------------------------------------------- /source/views/hstack_layout.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "warn_cpp/suppress_warnings.h" 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "vstgui/lib/cviewcontainer.h" 8 | #include "vstgui/lib/iviewlistener.h" 9 | END_SUPPRESS_WARNINGS 10 | 11 | namespace VSTGUI { 12 | class CViewContainer; 13 | class CView; 14 | struct CRect; 15 | } // namespace VSTGUI 16 | 17 | namespace mam { 18 | //------------------------------------------------------------------------ 19 | // HStackLayout 20 | //------------------------------------------------------------------------ 21 | class HStackLayout : public VSTGUI::ViewListenerAdapter, 22 | public VSTGUI::ViewContainerListenerAdapter 23 | { 24 | public: 25 | //-------------------------------------------------------------------- 26 | using ViewContainer = VSTGUI::CViewContainer; 27 | using View = VSTGUI::CView; 28 | using Rect = VSTGUI::CRect; 29 | using Coord = VSTGUI::CCoord; 30 | 31 | struct Padding 32 | { 33 | Coord top = 0.; 34 | Coord right = 0.; 35 | Coord bottom = 0.; 36 | Coord left = 0.; 37 | }; 38 | 39 | struct Spacing 40 | { 41 | Coord horiz = 0.; 42 | Coord verti = 0.; 43 | }; 44 | 45 | HStackLayout(ViewContainer* container); 46 | ~HStackLayout() override; 47 | 48 | void setup(const Spacing& spacing, const Padding& padding); 49 | 50 | void viewContainerViewAdded(ViewContainer* container, View* view) override; 51 | void viewContainerViewRemoved(ViewContainer* container, 52 | View* view) override; 53 | void viewSizeChanged(View* view, const Rect& oldSize) override; 54 | void viewWillDelete(View* view) override; 55 | 56 | //-------------------------------------------------------------------- 57 | private: 58 | ViewContainer* observed_container = nullptr; 59 | Spacing spacing{0., 0.}; 60 | Padding padding{0., 0., 0., 0.}; 61 | }; 62 | //------------------------------------------------------------------------ 63 | 64 | } // namespace mam 65 | -------------------------------------------------------------------------------- /source/views/spinner_view.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "spinner_view.h" 4 | #include "warn_cpp/suppress_warnings.h" 5 | #include 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "vstgui/lib/cdrawcontext.h" 8 | END_SUPPRESS_WARNINGS 9 | 10 | using namespace VSTGUI; 11 | 12 | namespace mam { 13 | 14 | #ifndef kPI 15 | #define kPI 3.14159265358979323846 16 | #endif 17 | 18 | //------------------------------------------------------------------------ 19 | auto inset_for_round_line_caps(CRect& size, const CCoord line_width) -> CRect& 20 | { 21 | size.left += std::ceil(line_width / 2.); 22 | size.right -= std::ceil(line_width / 2.); 23 | size.top += std::ceil(line_width / 2.); 24 | size.bottom -= std::ceil(line_width / 2.); 25 | 26 | return size; 27 | } 28 | 29 | //------------------------------------------------------------------------ 30 | struct Point 31 | { 32 | double x = 0.; 33 | double y = 0.; 34 | }; 35 | 36 | struct Rect 37 | { 38 | double width = 0.; 39 | double height = 0.; 40 | }; 41 | 42 | using Line = std::pair; 43 | using FnLine = std::function; 44 | /** 45 | * @brief Computes the lines to be drawn 46 | * @param rect The rect inside which the lines shall be drawn 47 | * @param centerPoint The center point coordinates inside the rect 48 | * @param angleDeltaNormalized Rotate the lines around the center, range 0..1 49 | * (e.g. for animation) 50 | * @param fnLine Function callback for each line 51 | */ 52 | auto compute_lines(const Rect& rect, 53 | const Point& center, 54 | double angleDeltaNormalized, 55 | FnLine&& fnLine) -> void 56 | { 57 | constexpr auto MAX_CIRCLE_ANGLE = 360.; 58 | constexpr auto HALF_CIRCLE_ANGLE = 180.; 59 | constexpr auto NUM_LINES = std::max(1, 12); // at least one line 60 | constexpr auto ANGLE_STEP = MAX_CIRCLE_ANGLE / NUM_LINES; 61 | constexpr double MAX_ANGLE = 62 | (NUM_LINES * ANGLE_STEP) * (kPI / HALF_CIRCLE_ANGLE); 63 | constexpr auto NORM_LINE_LEN = 0.3; // normalized from 0. to 1. 64 | 65 | const auto angleDeltaDegree = angleDeltaNormalized * MAX_CIRCLE_ANGLE; 66 | const auto width_half = rect.width * 0.5; 67 | const auto height_half = rect.height * 0.5; 68 | 69 | for (int i = 0; i < NUM_LINES; ++i) 70 | { 71 | const auto angle = (double(i) * ANGLE_STEP + angleDeltaDegree) * 72 | (kPI / HALF_CIRCLE_ANGLE); 73 | const auto width = sin(angle - kPI) * width_half; 74 | const auto height = cos(angle - kPI) * height_half; 75 | const Point start{center.x + width, center.y + height}; 76 | const Point end{center.x + width * (1. - NORM_LINE_LEN), 77 | center.y + height * (1. - NORM_LINE_LEN)}; 78 | 79 | const auto delta = angle / MAX_ANGLE; 80 | const Line line{start, end}; 81 | if (fnLine) 82 | fnLine(line, delta); 83 | } 84 | } 85 | //------------------------------------------------------------------------ 86 | auto draw_spinner_like_ios(CDrawContext* context, 87 | const CRect& bounds_, 88 | double animPos) -> void 89 | { 90 | constexpr auto USE_ROUND_CAPS = true; 91 | constexpr CCoord NORM_LINE_WIDTH = 0.1; // normalized from 0. to 1. 92 | 93 | CRect bounds = bounds_; 94 | if (USE_ROUND_CAPS) 95 | { 96 | // When using round caps, we need to have an inset to avoid clipping 97 | bounds = inset_for_round_line_caps(bounds, 98 | NORM_LINE_WIDTH * bounds.getWidth()); 99 | context->setLineStyle( 100 | CLineStyle(CLineStyle::kLineCapRound, CLineStyle::kLineJoinRound)); 101 | } 102 | 103 | context->setDrawMode(kAntiAliasing); 104 | context->setLineWidth(NORM_LINE_WIDTH * bounds.getWidth()); 105 | 106 | const Point centerPoint{bounds.getCenter().x, bounds.getCenter().y}; 107 | const Rect boundingBox{bounds.getWidth(), bounds.getHeight()}; 108 | 109 | compute_lines(boundingBox, centerPoint, 0., 110 | [context, animPos](const Line& line, double delta) { 111 | constexpr CColor LINE_COLOR(150, 150, 150); 112 | const auto newAlpha = 1. - fmod(animPos + delta, 1.); 113 | 114 | auto newLineColor = LINE_COLOR; 115 | newLineColor.setNormAlpha(newAlpha); 116 | context->setFrameColor(newLineColor); 117 | context->setFillColor(newLineColor); 118 | 119 | const CPoint start{line.first.x, line.first.y}; 120 | const CPoint end{line.second.x, line.second.y}; 121 | context->drawLine({start, end}); 122 | }); 123 | } 124 | 125 | //------------------------------------------------------------------------ 126 | SpinnerView::SpinnerView(const CRect& size) 127 | : CView(size) 128 | { 129 | } 130 | 131 | //------------------------------------------------------------------------ 132 | auto SpinnerView::set_animation_position(float position) -> void 133 | { 134 | animation_position = position; 135 | invalid(); 136 | } 137 | 138 | //------------------------------------------------------------------------ 139 | void SpinnerView::draw(CDrawContext* context) 140 | { 141 | CView::draw(context); 142 | 143 | draw_spinner_like_ios(context, getViewSize(), animation_position); 144 | } 145 | 146 | //------------------------------------------------------------------------ 147 | // SpinAnimation 148 | //------------------------------------------------------------------------ 149 | const char* SpinAnimation::ANIMATION_ID = "SpinAnimation"; 150 | SpinAnimation::SpinAnimation() {} 151 | 152 | //------------------------------------------------------------------------ 153 | void SpinAnimation::animationStart(CView* view, IdStringPtr name) 154 | { 155 | SpinnerView* spinner_view = dynamic_cast(view); 156 | if (!spinner_view) 157 | return; 158 | 159 | if (UTF8String(name) == ANIMATION_ID) 160 | { 161 | spinner_view->set_animation_position(0.); 162 | } 163 | } 164 | 165 | //------------------------------------------------------------------------ 166 | void SpinAnimation::animationTick(CView* view, IdStringPtr name, float pos) 167 | { 168 | SpinnerView* spinner_view = dynamic_cast(view); 169 | if (!spinner_view) 170 | return; 171 | 172 | if (UTF8String(name) == ANIMATION_ID) 173 | { 174 | spinner_view->set_animation_position(pos); 175 | } 176 | } 177 | 178 | //------------------------------------------------------------------------ 179 | void SpinAnimation::animationFinished(CView* view, 180 | IdStringPtr name, 181 | bool /*wasCanceled*/) 182 | { 183 | SpinnerView* spinner_view = dynamic_cast(view); 184 | if (!spinner_view) 185 | return; 186 | 187 | if (UTF8String(name) == ANIMATION_ID) 188 | { 189 | spinner_view->set_animation_position(1.); 190 | } 191 | } 192 | 193 | //------------------------------------------------------------------------ 194 | } // namespace mam 195 | -------------------------------------------------------------------------------- /source/views/spinner_view.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "warn_cpp/suppress_warnings.h" 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "vstgui/lib/animation/ianimationtarget.h" 8 | #include "vstgui/lib/cview.h" 9 | END_SUPPRESS_WARNINGS 10 | 11 | namespace mam { 12 | 13 | //------------------------------------------------------------------------ 14 | // SpinnerView 15 | //------------------------------------------------------------------------ 16 | class SpinnerView : public VSTGUI::CView 17 | { 18 | public: 19 | //-------------------------------------------------------------------- 20 | using Degree = double; 21 | 22 | SpinnerView(const VSTGUI::CRect& size); 23 | 24 | auto set_animation_position(float position) -> void; 25 | void draw(VSTGUI::CDrawContext* context) override; 26 | 27 | //-------------------------------------------------------------------- 28 | private: 29 | float animation_position = 0.; 30 | }; 31 | 32 | //------------------------------------------------------------------------ 33 | // SpinAnimation 34 | //------------------------------------------------------------------------ 35 | class SpinAnimation : public VSTGUI::Animation::IAnimationTarget, 36 | public VSTGUI::NonAtomicReferenceCounted 37 | { 38 | public: 39 | //-------------------------------------------------------------------- 40 | static const char* ANIMATION_ID; 41 | 42 | SpinAnimation(); 43 | 44 | void animationStart(VSTGUI::CView* view, VSTGUI::IdStringPtr name) override; 45 | void animationTick(VSTGUI::CView* view, 46 | VSTGUI::IdStringPtr name, 47 | float pos) override; 48 | void animationFinished(VSTGUI::CView* view, 49 | VSTGUI::IdStringPtr name, 50 | bool wasCanceled) override; 51 | //-------------------------------------------------------------------- 52 | private: 53 | }; 54 | 55 | //------------------------------------------------------------------------ 56 | } // namespace mam 57 | -------------------------------------------------------------------------------- /source/views/view_animations.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "view_animations.h" 4 | #include "warn_cpp/suppress_warnings.h" 5 | BEGIN_SUPPRESS_WARNINGS 6 | #include "vstgui/lib/animation/animations.h" 7 | #include "vstgui/lib/animation/timingfunctions.h" 8 | #include "vstgui/lib/cview.h" 9 | END_SUPPRESS_WARNINGS 10 | 11 | namespace mam::animations { 12 | 13 | //------------------------------------------------------------------------ 14 | constexpr auto DUR = 300; 15 | 16 | //------------------------------------------------------------------------ 17 | auto add_simple_fade_in(View* view) -> void 18 | { 19 | namespace Anim = VSTGUI::Animation; 20 | 21 | if (view) 22 | { 23 | view->addAnimation("AlphaAnimation", 24 | new Anim::AlphaValueAnimation(1.f, true), 25 | new Anim::CubicBezierTimingFunction( 26 | Anim::CubicBezierTimingFunction::easyIn(DUR))); 27 | } 28 | } 29 | 30 | //------------------------------------------------------------------------ 31 | 32 | auto add_simple_fade_out(View* view) -> void 33 | { 34 | namespace Anim = VSTGUI::Animation; 35 | 36 | if (view) 37 | { 38 | view->addAnimation( 39 | "AlphaAnimation", new Anim::AlphaValueAnimation(0.f, true), 40 | new Anim::CubicBezierTimingFunction( 41 | Anim::CubicBezierTimingFunction::easyOut(DUR * 2))); 42 | } 43 | } 44 | 45 | //------------------------------------------------------------------------ 46 | } // namespace mam::animations -------------------------------------------------------------------------------- /source/views/view_animations.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | namespace VSTGUI { 6 | class CView; 7 | } 8 | 9 | namespace mam::animations { 10 | 11 | //------------------------------------------------------------------------ 12 | using View = VSTGUI::CView; 13 | 14 | auto add_simple_fade_in(View* view) -> void; 15 | auto add_simple_fade_out(View* view) -> void; 16 | 17 | //------------------------------------------------------------------------ 18 | } // namespace mam::animations -------------------------------------------------------------------------------- /source/views/waveform_view.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "waveform_view.h" 4 | #include "little_helpers.h" 5 | #include "mam/wave-draw/wave-draw.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | #include 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "vstgui/lib/cdrawcontext.h" 10 | #include "vstgui/lib/cgraphicspath.h" 11 | END_SUPPRESS_WARNINGS 12 | 13 | using namespace VSTGUI; 14 | 15 | namespace mam { 16 | 17 | //------------------------------------------------------------------------ 18 | constexpr auto HITLITE_TINT_FACTOR = 2.f / 3.f; 19 | constexpr auto SPACING = 1.; 20 | constexpr auto LINE_WIDTH = 2.; 21 | constexpr auto ROUND_CORNER_RADIUS = 1.; 22 | 23 | //------------------------------------------------------------------------ 24 | WaveFormView::WaveFormView(const CRect& size) 25 | : CView(size) 26 | { 27 | } 28 | 29 | //------------------------------------------------------------------------ 30 | auto WaveFormView::draw_like_spotify(CDrawContext& pContext, 31 | const CRect& viewSize) -> void 32 | { 33 | if (!waveform_data_func) 34 | return; 35 | 36 | using Drawer = wave_draw::Drawer; 37 | using DrawData = wave_draw::DrawData; 38 | using Color = VSTGUI::CColor; 39 | 40 | auto waveform_data = waveform_data_func(); 41 | // Since we have a fixed view_width, we need to compute the zoom_factor 42 | // beforehand. 43 | const auto zoom_factor = wave_draw::compute_zoom_factor( 44 | waveform_data.audio_buffer, viewSize.getWidth(), LINE_WIDTH, SPACING); 45 | 46 | const auto samples_per_bucket = static_cast(zoom_factor); 47 | const BoundsCheck bounds_check( 48 | {waveform_data.hilite_range.first / samples_per_bucket, 49 | waveform_data.hilite_range.second / samples_per_bucket}); 50 | 51 | // TODO: Get rid of warning! 52 | const auto& [r, g, b] = waveform_data.color; 53 | const Color color_normal = make_color(r, g, b, std::nullopt); 54 | const Color color_hilite = make_color(r, g, b, HITLITE_TINT_FACTOR); 55 | 56 | auto normal_graphics_path = owned(pContext.createGraphicsPath()); 57 | auto hilite_graphics_path = owned(pContext.createGraphicsPath()); 58 | 59 | Drawer(waveform_data.audio_buffer, zoom_factor) 60 | .setup_wave(LINE_WIDTH, SPACING) 61 | .setup_dimensions(viewSize.getWidth(), viewSize.getHeight()) 62 | .draw([&](const DrawData& data, size_t count) { 63 | const auto rect = 64 | CRect({data.x, data.y}, {data.width, data.height}); 65 | 66 | if (bounds_check.is_in(count)) 67 | hilite_graphics_path->addRoundRect(rect, ROUND_CORNER_RADIUS); 68 | else 69 | normal_graphics_path->addRoundRect(rect, ROUND_CORNER_RADIUS); 70 | }); 71 | 72 | pContext.setFillColor(color_normal); 73 | pContext.drawGraphicsPath(normal_graphics_path); 74 | pContext.setFillColor(color_hilite); 75 | pContext.drawGraphicsPath(hilite_graphics_path); 76 | } 77 | 78 | //------------------------------------------------------------------------ 79 | auto WaveFormView::drawFull(CDrawContext* pContext, 80 | const CRect& viewSize) -> void 81 | { 82 | if (!waveform_data_func) 83 | return; 84 | 85 | pContext->setLineWidth(1.0); 86 | 87 | const auto amplitude = viewSize.getHeight() * 0.5; 88 | const auto waveFormData = waveform_data_func().audio_buffer; 89 | const auto numSamples = waveform_data_func().audio_buffer.size(); 90 | if (numSamples > 1) 91 | { 92 | // Calculate the horizontal scale factor 93 | const auto xScale = 94 | viewSize.getWidth() / static_cast(numSamples - 1); 95 | 96 | size_t stylized = 1; // from 1 up to x to have it more stylized 97 | // Draw the waveform lines 98 | for (size_t i = 0; i < size_t(numSamples) - 1; i += stylized) 99 | { 100 | const auto x1 = CCoord(i) * xScale; 101 | const auto y1 = amplitude * CCoord(waveFormData[i]); 102 | const auto x2 = CCoord(i + stylized) * xScale; 103 | const auto y2 = amplitude * CCoord(waveFormData[i + 1]); 104 | 105 | pContext->drawLine(CPoint(x1, amplitude + y1), 106 | CPoint(x2, amplitude + y2)); 107 | } 108 | } 109 | } 110 | 111 | //------------------------------------------------------------------------ 112 | // https://steinbergmedia.github.io/vst3_doc/vstgui/html/the_view_system.html#inherit_from_cview 113 | //------------------------------------------------------------------------ 114 | auto WaveFormView::draw(CDrawContext* pContext) -> void 115 | { 116 | if (!pContext) 117 | return; 118 | 119 | const auto viewSize = getViewSize(); 120 | CDrawContext::Transform t( 121 | *pContext, CGraphicsTransform().translate(viewSize.getTopLeft())); 122 | 123 | draw_like_spotify(*pContext, viewSize); 124 | } 125 | 126 | //------------------------------------------------------------------------ 127 | } // namespace mam 128 | -------------------------------------------------------------------------------- /source/views/waveform_view.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "nonstd.h" 6 | #include "warn_cpp/suppress_warnings.h" 7 | BEGIN_SUPPRESS_WARNINGS 8 | #include "vstgui/lib/ccolor.h" 9 | #include "vstgui/lib/cview.h" 10 | END_SUPPRESS_WARNINGS 11 | 12 | namespace mam { 13 | 14 | //------------------------------------------------------------------------ 15 | // WaveFormView 16 | //------------------------------------------------------------------------ 17 | class WaveFormView : public VSTGUI::CView 18 | { 19 | public: 20 | //-------------------------------------------------------------------- 21 | struct Data 22 | { 23 | using Color = std::tuple; 24 | using AudioBuffer = nonstd::span; 25 | using Range = std::pair; // start and duration 26 | 27 | Color color; 28 | AudioBuffer audio_buffer; 29 | Range hilite_range; 30 | }; 31 | 32 | using FuncWaveFormData = std::function; 33 | FuncWaveFormData waveform_data_func; 34 | 35 | WaveFormView(const VSTGUI::CRect& size); 36 | auto draw(VSTGUI::CDrawContext* pContext) -> void override; 37 | 38 | //-------------------------------------------------------------------- 39 | private: 40 | auto drawFull(VSTGUI::CDrawContext* pContext, const VSTGUI::CRect& viewSize) 41 | -> void; 42 | auto draw_like_spotify(VSTGUI::CDrawContext& pContext, 43 | const VSTGUI::CRect& viewSize) -> void; 44 | }; 45 | 46 | } // namespace mam 47 | -------------------------------------------------------------------------------- /source/views/word_button.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "warn_cpp/suppress_warnings.h" 4 | #include "word_button.h" 5 | BEGIN_SUPPRESS_WARNINGS 6 | #include "vstgui/lib/cdrawcontext.h" 7 | #include "vstgui/uidescription/uidescription.h" 8 | END_SUPPRESS_WARNINGS 9 | 10 | using namespace VSTGUI; 11 | 12 | namespace mam { 13 | namespace { 14 | 15 | //------------------------------------------------------------------------ 16 | auto drawBackground(CDrawContext* context, 17 | const CRect& rect, 18 | const CCoord radius, 19 | const CColor background) -> void 20 | { 21 | context->setFillColor(background); 22 | if (auto path = owned(context->createRoundRectGraphicsPath(rect, radius))) 23 | context->drawGraphicsPath(path); 24 | } 25 | 26 | //------------------------------------------------------------------------ 27 | } // namespace 28 | 29 | //------------------------------------------------------------------------ 30 | // WordButton 31 | //------------------------------------------------------------------------ 32 | WordButton::WordButton(const CRect& size, 33 | IControlListener* listener, 34 | int32_t tag, 35 | UTF8StringPtr title, 36 | Style style) 37 | : CTextButton(size, listener, tag, title, style) 38 | { 39 | } 40 | 41 | //------------------------------------------------------------------------ 42 | void WordButton::draw(CDrawContext* context) 43 | { 44 | if (state != State::kNone) 45 | { 46 | drawBackground(context, getViewSize(), getRoundRadius(), 47 | currentBgrColor); 48 | } 49 | 50 | CTextButton::draw(context); 51 | } 52 | 53 | //------------------------------------------------------------------------ 54 | bool WordButton::setState(State new_state) 55 | { 56 | const bool changed = state != new_state; 57 | state = new_state; 58 | if (!changed) 59 | return false; 60 | 61 | switch (state) 62 | { 63 | case State::kNone: { 64 | setTextColor(normalTextColor); 65 | currentBgrColor = VSTGUI::kTransparentCColor; 66 | break; 67 | } 68 | 69 | case State::kSearched: { 70 | setTextColor(searchedTextColor); 71 | currentBgrColor = searchedBgrColor; 72 | break; 73 | } 74 | 75 | case State::kFocused: { 76 | setTextColor(focusedTextColor); 77 | currentBgrColor = focusedBgrColor; 78 | break; 79 | } 80 | 81 | default: { 82 | setTextColor(normalTextColor); 83 | currentBgrColor = VSTGUI::kTransparentCColor; 84 | break; 85 | } 86 | } 87 | 88 | return changed; 89 | } 90 | 91 | //------------------------------------------------------------------------ 92 | void WordButton::verifyTextButtonView(const VSTGUI::IUIDescription* description) 93 | { 94 | description->getColor("search_hilite_bgr_color", searchedBgrColor); 95 | description->getColor("search_hilite_text_color", searchedTextColor); 96 | description->getColor("search_select_hilite_bgr_color", focusedBgrColor); 97 | description->getColor("search_select_hilite_text_color", focusedTextColor); 98 | description->getColor("transcript_text_color", normalTextColor); 99 | } 100 | 101 | //------------------------------------------------------------------------ 102 | } // namespace mam 103 | -------------------------------------------------------------------------------- /source/views/word_button.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "warn_cpp/suppress_warnings.h" 6 | BEGIN_SUPPRESS_WARNINGS 7 | #include "vstgui/lib/controls/cbuttons.h" 8 | END_SUPPRESS_WARNINGS 9 | 10 | namespace VSTGUI { 11 | class IUIDescription; 12 | } // namespace VSTGUI 13 | 14 | namespace mam { 15 | 16 | //------------------------------------------------------------------------ 17 | // WordButton 18 | //------------------------------------------------------------------------ 19 | class WordButton : public VSTGUI::CTextButton 20 | { 21 | public: 22 | //-------------------------------------------------------------------- 23 | enum class State 24 | { 25 | kNone = 0, 26 | kSearched, 27 | kFocused 28 | }; 29 | 30 | WordButton(const VSTGUI::CRect& size, 31 | VSTGUI::IControlListener* listener = nullptr, 32 | int32_t tag = -1, 33 | VSTGUI::UTF8StringPtr title = nullptr, 34 | VSTGUI::CTextButton::Style = kKickStyle); 35 | 36 | void draw(VSTGUI::CDrawContext* context) override; 37 | bool setState(State state); 38 | void verifyTextButtonView(const VSTGUI::IUIDescription* description); 39 | 40 | //-------------------------------------------------------------------- 41 | private: 42 | VSTGUI::CColor searchedBgrColor = VSTGUI::kGreyCColor; 43 | VSTGUI::CColor searchedTextColor = VSTGUI::kWhiteCColor; 44 | VSTGUI::CColor focusedBgrColor = VSTGUI::kYellowCColor; 45 | VSTGUI::CColor focusedTextColor = VSTGUI::kBlackCColor; 46 | VSTGUI::CColor normalTextColor = VSTGUI::kBlackCColor; 47 | VSTGUI::CColor currentBgrColor = VSTGUI::kTransparentCColor; 48 | State state = State::kNone; 49 | }; 50 | 51 | //------------------------------------------------------------------------ 52 | } // namespace mam 53 | -------------------------------------------------------------------------------- /source/whipser_cpp_wrapper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #include "whipser_cpp_wrapper.h" 4 | #include "hao/special_folders/special_folders.h" 5 | #include "warn_cpp/suppress_warnings.h" 6 | #include "wordify_defines.h" 7 | #include 8 | BEGIN_SUPPRESS_WARNINGS 9 | #include "base/source/fdebug.h" 10 | #include "whereami.h" 11 | END_SUPPRESS_WARNINGS 12 | 13 | using namespace hao::special_folders; 14 | 15 | namespace mam::whisper_cpp { 16 | 17 | //------------------------------------------------------------------------ 18 | namespace { 19 | 20 | //------------------------------------------------------------------------ 21 | auto get_ggml_file_path(const StringType& company_name, 22 | const StringType& plugin_name) -> PathType 23 | { 24 | #if DEVELOPMENT 25 | (void)company_name; 26 | (void)plugin_name; 27 | std::filesystem::path file_path(MAM_WHISPER_CPP_MODEL_DOWNLOAD_DIR); 28 | file_path /= "ggml-base.en.bin"; 29 | #else 30 | const auto model_path_str = get_application_data_folder(Domain::kLocal); 31 | 32 | std::filesystem::path file_path(model_path_str); 33 | file_path /= company_name; 34 | file_path /= plugin_name; 35 | file_path /= MAM_GGML_DIRECTORY_NAME; 36 | file_path /= "ggml-medium.bin"; 37 | #endif 38 | return file_path.string(); 39 | } 40 | 41 | //------------------------------------------------------------------------ 42 | } // namespace 43 | 44 | //------------------------------------------------------------------------ 45 | auto get_worker_executable_path() -> PathType 46 | { 47 | #if DEVELOPMENT 48 | return MAM_WHISPER_CPP_EXECUTABLE; 49 | #else 50 | const int length = wai_getModulePath(NULL, 0, NULL); 51 | const auto cpath = (char*)malloc(length + 1); 52 | wai_getModulePath(cpath, length, NULL); 53 | cpath[length] = '\0'; 54 | 55 | std::filesystem::path fs_path(cpath); 56 | free(cpath); 57 | 58 | fs_path.replace_filename(MAM_WHISPER_CPP_EXECUTABLE_NAME); 59 | return fs_path.string(); 60 | #endif 61 | } 62 | 63 | //------------------------------------------------------------------------ 64 | auto get_ggml_file_path() -> PathType 65 | { 66 | return get_ggml_file_path(COMPANY_NAME_STR, PLUGIN_NAME_STR); 67 | } 68 | 69 | //------------------------------------------------------------------------ 70 | 71 | } // namespace mam::whisper_cpp 72 | -------------------------------------------------------------------------------- /source/whipser_cpp_wrapper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include "wordify_types.h" 6 | 7 | namespace mam::whisper_cpp { 8 | //------------------------------------------------------------------------ 9 | using PathType = StringType; 10 | 11 | auto get_worker_executable_path() -> PathType; 12 | auto get_ggml_file_path() -> PathType; 13 | 14 | //------------------------------------------------------------------------ 15 | } // namespace mam::whisper_cpp -------------------------------------------------------------------------------- /source/wordify_cids.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "pluginterfaces/base/funknown.h" 8 | #include "pluginterfaces/vst/vsttypes.h" 9 | 10 | namespace mam { 11 | //------------------------------------------------------------------------ 12 | static const Steinberg::FUID 13 | kWordifyProcessorUID(0x76FA7B01, 0x4D2757B4, 0x9BA55204, 0x681B0F2C); 14 | static const Steinberg::FUID 15 | kWordifyControllerUID(0xB7046436, 0x1AFA58A3, 0x99C5BA83, 0x7ED9EC1F); 16 | 17 | #define WordifyVST3Category "OnlyARA" 18 | 19 | //------------------------------------------------------------------------ 20 | } // namespace mam 21 | -------------------------------------------------------------------------------- /source/wordify_defines.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #define PLUGIN_NAME_STR PROJECT_NAME 8 | #define COMPANY_NAME_STR CPACK_PACKAGE_VENDOR 9 | #define COMPANY_URL_STR PROJECT_HOMEPAGE_URL 10 | #define COMPANY_EMAIL_STR PROJECT_CONTACT_EMAIL 11 | #define PLUGIN_IDENTIFIER PROJECT_BUNDLE_IDENTIFIER 12 | -------------------------------------------------------------------------------- /source/wordify_entry.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #include "ara_main_factory.h" 6 | #include "public.sdk/source/main/pluginfactory.h" 7 | #include "version.h" 8 | #include "wordify_cids.h" 9 | #include "wordify_defines.h" 10 | #include "wordify_single_component.h" 11 | 12 | using namespace Steinberg::Vst; 13 | using namespace mam; 14 | 15 | //------------------------------------------------------------------------ 16 | // VST Plug-in Entry 17 | //------------------------------------------------------------------------ 18 | DEF_CLASS_IID(ARA::IMainFactory) 19 | DEF_CLASS_IID(ARA::IPlugInEntryPoint) 20 | DEF_CLASS_IID(ARA::IPlugInEntryPoint2) 21 | 22 | // clang-format off 23 | 24 | BEGIN_FACTORY_DEF(COMPANY_NAME_STR, 25 | COMPANY_URL_STR, 26 | COMPANY_EMAIL_STR) 27 | 28 | //---First Plug-in included in this factory------- 29 | DEF_CLASS2(INLINE_UID_FROM_FUID(kWordifyProcessorUID), 30 | PClassInfo::kManyInstances, // cardinality 31 | kVstAudioEffectClass, // the component category (do not changed this) 32 | PLUGIN_NAME_STR, // here the Plug-in name (to be changed) 33 | Vst::kDistributable, // means that component and controller could be distributed on different computers 34 | WordifyVST3Category, // Subcategory for this Plug-in (to be changed) 35 | FULL_VERSION_STR, // Plug-in version (to be changed) 36 | kVstVersionString, // the VST 3 SDK version (do not changed this, use always this define) 37 | WordifySingleComponent::createInstance) // function pointer called when this component should be instantiated 38 | 39 | // its kARAMainFactoryClass component 40 | DEF_CLASS2(INLINE_UID_FROM_FUID(ARAMainFactory::getClassFUID()), 41 | PClassInfo::kManyInstances, // cardinality 42 | kARAMainFactoryClass, // the ARA Main Factory category (do not changed this) 43 | PLUGIN_NAME_STR "ARA Factory", // here the Plug-in name (MUST be the same as component name if multiple kVstAudioEffectClass components are used!) 44 | 0, // not used here 45 | "", // not used here 46 | FULL_VERSION_STR, // Plug-in version (to be changed) 47 | kVstVersionString, // the VST 3 SDK version (do not changed this, use always this define) 48 | ARAMainFactory::createInstance) // function pointer called when this component should be instantiated 49 | 50 | //----for others Plug-ins contained in this factory, put like for the first Plug-in different DEF_CLASS2--- 51 | 52 | END_FACTORY 53 | 54 | // clang-format on 55 | -------------------------------------------------------------------------------- /source/wordify_single_component.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright (c) 2023-present, WordifyOrg. 3 | //------------------------------------------------------------------------ 4 | 5 | #pragma once 6 | 7 | #include "warn_cpp/suppress_warnings.h" 8 | #include "task_manager.h" 9 | BEGIN_SUPPRESS_WARNINGS 10 | #include "ARA_API/ARAVST3.h" 11 | #include "ARA_Library/PlugIn/ARAPlug.h" 12 | #include "ipslviewembedding.h" 13 | #include "public.sdk/source/vst/vstsinglecomponenteffect.h" 14 | #include "vstgui/plugin-bindings/vst3editor.h" 15 | END_SUPPRESS_WARNINGS 16 | 17 | namespace mam { 18 | 19 | //------------------------------------------------------------------------ 20 | // WordifySingleComponent 21 | //------------------------------------------------------------------------ 22 | class WordifySingleComponent : public Steinberg::Vst::SingleComponentEffect, 23 | public ARA::IPlugInEntryPoint, 24 | public ARA::IPlugInEntryPoint2, 25 | public VSTGUI::VST3EditorDelegate, 26 | public Presonus::IPlugInViewEmbedding 27 | { 28 | public: 29 | //-------------------------------------------------------------------- 30 | 31 | using Editors = std::vector; 32 | 33 | WordifySingleComponent(); 34 | ~WordifySingleComponent() override; 35 | 36 | // Create function 37 | static Steinberg::FUnknown* createInstance(void* /*context*/) 38 | { 39 | return (Steinberg::Vst::IAudioProcessor*)new WordifySingleComponent; 40 | } 41 | 42 | // AudioEffect overrides: 43 | /** Called at first after constructor */ 44 | Steinberg::tresult PLUGIN_API 45 | initialize(Steinberg::FUnknown* context) override; 46 | 47 | /** Called at the end before destructor */ 48 | Steinberg::tresult PLUGIN_API terminate() override; 49 | 50 | /** Switch the Plug-in on/off */ 51 | Steinberg::tresult PLUGIN_API setActive(Steinberg::TBool state) override; 52 | 53 | /** Will be called before any process call */ 54 | Steinberg::tresult PLUGIN_API 55 | setupProcessing(Steinberg::Vst::ProcessSetup& newSetup) override; 56 | 57 | /** Asks if a given sample size is supported see SymbolicSampleSizes. */ 58 | Steinberg::tresult PLUGIN_API 59 | canProcessSampleSize(Steinberg::int32 symbolicSampleSize) override; 60 | 61 | /** Here we go...the process call */ 62 | Steinberg::tresult PLUGIN_API 63 | process(Steinberg::Vst::ProcessData& data) override; 64 | 65 | /** For persistence */ 66 | Steinberg::tresult PLUGIN_API setState(Steinberg::IBStream* state) override; 67 | Steinberg::tresult PLUGIN_API getState(Steinberg::IBStream* state) override; 68 | 69 | // ARA::IPlugInEntryPoint2 overrides: 70 | /** Get associated ARA factory */ 71 | const ARA::ARAFactory* PLUGIN_API getFactory() override; 72 | 73 | /** Bind to ARA document controller instance */ 74 | const ARA::ARAPlugInExtensionInstance* PLUGIN_API bindToDocumentController( 75 | ARA::ARADocumentControllerRef documentControllerRef) override; 76 | const ARA::ARAPlugInExtensionInstance* PLUGIN_API 77 | bindToDocumentControllerWithRoles( 78 | ARA::ARADocumentControllerRef documentControllerRef, 79 | ARA::ARAPlugInInstanceRoleFlags knownRoles, 80 | ARA::ARAPlugInInstanceRoleFlags assignedRoles) override; 81 | 82 | // VSTGUI::VST3EditorDelegate 83 | VSTGUI::IController* 84 | createSubController(VSTGUI::UTF8StringPtr name, 85 | const VSTGUI::IUIDescription* description, 86 | VSTGUI::VST3Editor* editor) override; 87 | void didOpen(VSTGUI::VST3Editor* editor) override; 88 | void willClose(VSTGUI::VST3Editor* editor) override; 89 | 90 | // Edit Controller 91 | Steinberg::IPlugView* PLUGIN_API 92 | createView(Steinberg::FIDString name) override; 93 | void PLUGIN_API editorAttached(Steinberg::Vst::EditorView* editor) override; 94 | void PLUGIN_API editorRemoved(Steinberg::Vst::EditorView* editor) override; 95 | void PLUGIN_API update(Steinberg::FUnknown* changedUnknown, 96 | Steinberg::int32 tag) override; 97 | 98 | // Presonus 99 | Steinberg::TBool PLUGIN_API isViewEmbeddingSupported() override; 100 | Steinberg::tresult PLUGIN_API setViewIsEmbedded( 101 | Steinberg::IPlugView* view, Steinberg::TBool embedded) override; 102 | 103 | OBJ_METHODS(WordifySingleComponent, Steinberg::Vst::SingleComponentEffect) 104 | DEFINE_INTERFACES 105 | DEF_INTERFACE(IPlugInEntryPoint) 106 | DEF_INTERFACE(IPlugInEntryPoint2) 107 | DEF_INTERFACE(IPlugInViewEmbedding) 108 | END_DEFINE_INTERFACES(Steinberg::Vst::SingleComponentEffect) 109 | REFCOUNT_METHODS(Steinberg::Vst::SingleComponentEffect) 110 | 111 | //-------------------------------------------------------------------- 112 | protected: 113 | ARA::PlugIn::PlugInExtension araPlugInExtension; 114 | Editors editors; 115 | 116 | auto restore_parameters() -> void; 117 | auto store_parameters() -> void; 118 | 119 | bool dark_scheme = false; 120 | task_managing::TaskCountCallback::Handle task_count_handle; 121 | }; 122 | 123 | //------------------------------------------------------------------------ 124 | } // namespace mam 125 | -------------------------------------------------------------------------------- /source/wordify_types.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023-present, WordifyOrg. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace mam { 10 | //------------------------------------------------------------------------ 11 | using u64 = uint64_t; 12 | using Id = uint64_t; 13 | using Index = uint64_t; 14 | using OptionalId = std::optional; 15 | using StringType = std::string; 16 | 17 | //------------------------------------------------------------------------ 18 | } // namespace mam 19 | -------------------------------------------------------------------------------- /test/test_temp_dir.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------ 2 | // Copyright(c) 2023 Max And Me. 3 | //------------------------------------------------------------------------ 4 | 5 | // https://en.cppreference.com/w/cpp/filesystem/temp_directory_path 6 | 7 | #include 8 | #include 9 | namespace fs = std::filesystem; 10 | 11 | int main() 12 | { 13 | std::cout << "Temp directory is " << fs::temp_directory_path() << '\n'; 14 | } --------------------------------------------------------------------------------