diff --git a/.editorconfig b/.editorconfig index a219353e4..433a8f65d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,7 @@ indent_style = space # Code files [*.{cs,csx,vb,vbx}] -indent_size = 4 +indent_size = 4 insert_final_newline = true charset = utf-8-bom guidelines = 100 @@ -18,74 +18,83 @@ guidelines = 100 # Organize usings dotnet_sort_system_directives_first = true # this. preferences -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_property = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent # Language keywords vs BCL types preferences dotnet_style_predefined_type_for_locals_parameters_members = true:silent dotnet_style_predefined_type_for_member_access = true:silent # Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent -dotnet_style_readonly_field = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion # Expression-level preferences -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent dotnet_prefer_inferred_tuple_names = true:suggestion dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent ############################### # Naming Conventions # ############################### # Style Definitions dotnet_naming_style.pascal_case_style.capitalization = pascal_case # Use PascalCase for constant fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.applicable_accessibilities = * dotnet_naming_symbols.constant_fields.required_modifiers = const +tab_width = 4 +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_namespace_match_folder = true:suggestion ############################### # C# Coding Conventions # ############################### [*.cs] # var preferences -csharp_style_var_for_built_in_types = true:silent -csharp_style_var_when_type_is_apparent = true:silent -csharp_style_var_elsewhere = true:silent +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent # Expression-bodied members -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent # Pattern matching preferences -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion # Null-checking preferences -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion # Modifier preferences csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion # Expression-level preferences -csharp_prefer_braces = true:silent -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_prefer_simple_default_expression = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion csharp_style_pattern_local_over_anonymous_function = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion ############################### # C# Formatting Rules # ############################### @@ -100,7 +109,7 @@ csharp_new_line_between_query_expression_clauses = false # Indentation preferences csharp_indent_case_contents = true csharp_indent_switch_labels = true -csharp_indent_labels = flush_left +csharp_indent_labels = flush_left # Space preferences csharp_space_after_cast = false csharp_space_after_keywords_in_control_flow_statements = true @@ -109,13 +118,36 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_after_colon_in_inheritance_clause = true -csharp_space_around_binary_operators = before_and_after +csharp_space_around_binary_operators = before_and_after csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_empty_parameter_list_parentheses = false # Wrapping preferences csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_static_anonymous_function = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion ############################### # VB Coding Conventions # ############################### diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0fd3f42b0..24f314e1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,17 +30,19 @@ jobs: fail-fast: false matrix: arch: - - { name: win-x64, rid: win-x64, arch: x64, os: win, runs-on: windows-latest } - - { name: win-x86, rid: win-x86, arch: x86, os: win, runs-on: windows-latest } - - { name: win-arm64, rid: win-arm64, arch: arm64, os: win, runs-on: windows-latest } - - { name: osx-x64, rid: osx-x64, arch: x64, os: osx, runs-on: macos-13 } - - { name: osx-arm64, rid: osx-arm64, arch: arm64, os: osx, runs-on: macos-13 } - - { name: linux-x64, rid: linux-x64, arch: x64, os: linux, runs-on: ubuntu-latest } - - { name: linux-arm64, rid: linux-arm64, arch: arm64, os: linux, runs-on: ubuntu-latest } + - { name: win-x64, rid: win-x64, arch: x64, os: win, runs-on: windows-latest, daw-plugin: true } + - { name: win-x86, rid: win-x86, arch: x86, os: win, runs-on: windows-latest, daw-plugin: false } + - { name: win-arm64, rid: win-arm64, arch: arm64, os: win, runs-on: windows-latest, daw-plugin: true } + - { name: osx-x64, rid: osx-x64, arch: x64, os: osx, runs-on: macos-13, daw-plugin: false } + - { name: osx-arm64, rid: osx-arm64, arch: arm64, os: osx, runs-on: macos-13, daw-plugin: false } + - { name: linux-x64, rid: linux-x64, arch: x64, os: linux, runs-on: ubuntu-24.04, daw-plugin: false } + - { name: linux-arm64, rid: linux-arm64, arch: arm64, os: linux, runs-on: ubuntu-24.04, daw-plugin: false } steps: # Setup - uses: actions/checkout@v4 + with: + submodules: recursive - uses: actions/setup-dotnet@v4 with: dotnet-version: | @@ -58,7 +60,7 @@ jobs: - name: Test run: dotnet test OpenUtau.Test - # Build + # Build: Main - name: Restore run: dotnet restore OpenUtau -r ${{ matrix.arch.rid }} @@ -66,6 +68,56 @@ jobs: run: dotnet publish OpenUtau -c Release -r ${{ matrix.arch.rid }} --self-contained true -o bin/${{ matrix.arch.name }}/ if: ${{ matrix.arch.os != 'osx' }} + # Build: Daw Plugin + + - name: Build Daw Plugin (Windows) + run: | + cd ./DawPlugin + cmake -B build -S . -DCMAKE_BUILD_TYPE=Release + cmake --build build --config Release + if: ${{ matrix.arch.daw-plugin && matrix.arch.os == 'win' }} + + - name: Build Daw Plugin (Mac) + run: | + cd ./DawPlugin + + brew install llvm + export PATH="/usr/local/opt/llvm/bin:$PATH" + export LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib -lunwind -fexperimental-library" + export CPPFLAGS="-I/usr/local/opt/llvm/include -fexperimental-library" + + cmake -B build -S . -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=/usr/local/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/opt/llvm/bin/clang++ \ + -DCMAKE_CXX_FLAGS="-stdlib=libc++ -fexperimental-library" + cmake --build build --config Release + if: ${{ matrix.arch.daw-plugin && matrix.arch.os == 'osx' }} + + - name: Build Daw Plugin (Linux) + run: | + cd ./DawPlugin + + # Install OpenGL + sudo apt-get install libgl1-mesa-dev + + cmake -B build -S . -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 + cmake --build build --config Release + if: ${{ matrix.arch.daw-plugin && matrix.arch.os == 'linux' }} + + - name: Package Daw Plugin + shell: bash + run: | + cd DawPlugin/build/bin + 7z a openutau_daw_plugin-${{ matrix.arch.name }}.vst3.zip openutau_daw_plugin.vst3 + if: ${{ matrix.arch.daw-plugin }} + + - name: Package Daw Plugin (Mac) + shell: bash + run: | + cd DawPlugin/build/bin + 7z a openutau_daw_plugin-${{ matrix.arch.name }}.au.zip openutau_daw_plugin.component + if: ${{ matrix.arch.daw-plugin && matrix.arch.os == 'osx' }} + # Create Zip - name: DirectML shell: cmd @@ -129,6 +181,18 @@ jobs: path: OpenUtau-${{ matrix.arch.name }}.dmg if: ${{ !inputs.release && matrix.arch.os == 'osx' }} + - uses: actions/upload-artifact@v4 + with: + name: openutau_daw_plugin-${{ matrix.arch.name }}.vst3.zip + path: DawPlugin/build/bin/openutau_daw_plugin-${{ matrix.arch.name }}.vst3.zip + if: ${{ !inputs.release && matrix.arch.daw-plugin }} + + - uses: actions/upload-artifact@v4 + with: + name: openutau_daw_plugin-${{ matrix.arch.name }}.au.zip + path: DawPlugin/build/bin/openutau_daw_plugin-${{ matrix.arch.name }}.au.zip + if: ${{ !inputs.release && matrix.arch.daw-plugin && matrix.arch.os == 'osx' }} + # Appcast - name: Appcast Windows shell: cmd @@ -158,6 +222,8 @@ jobs: files: | appcast.${{ matrix.arch.name }}*.xml OpenUtau-${{ matrix.arch.name }}.* + DawPlugin/build/bin/openutau_daw_plugin-${{ matrix.arch.name }}.vst3.zip + DawPlugin/build/bin/openutau_daw_plugin-${{ matrix.arch.name }}.au.zip body: | See [Getting Started](https://github.com/stakira/OpenUtau/wiki/Getting-Started) for how to use. diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..3aa5b86f9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,21 @@ +[submodule "DawPlugin/deps/dpf"] + path = DawPlugin/deps/dpf + url = https://github.com/distrho/DPF.git +[submodule "DawPlugin/deps/asio"] + path = DawPlugin/deps/asio + url = https://github.com/chriskohlhoff/asio +[submodule "DawPlugin/deps/dpf_widgets"] + path = DawPlugin/deps/dpf_widgets + url = https://github.com/sevenc-nanashi/DPF-Widgets.git +[submodule "DawPlugin/deps/choc"] + path = DawPlugin/deps/choc + url = https://github.com/Tracktion/choc +[submodule "DawPlugin/deps/uuid-v4"] + path = DawPlugin/deps/uuid-v4 + url = https://github.com/rkg82/uuid-v4.git +[submodule "DawPlugin/deps/yamc"] + path = DawPlugin/deps/yamc + url = https://github.com/yohhoy/yamc.git +[submodule "DawPlugin/deps/zstd"] + path = DawPlugin/deps/zstd + url = https://github.com/facebook/zstd diff --git a/DawPlugin/.cmake-format.json b/DawPlugin/.cmake-format.json new file mode 100644 index 000000000..6c5e8a0a2 --- /dev/null +++ b/DawPlugin/.cmake-format.json @@ -0,0 +1,23 @@ +{ + "additional_commands": { + "dpf_add_plugin": { + "pargs": 1, + "kwargs": { + "TARGETS": "*", + "UI_TYPE": 1, + "FILES_DSP": "*", + "FILES_UI": "*", + "FILES_COMMON": "*" + } + }, + "corrosion_import_crate": { + "pargs": 0, + "kwargs": { + "MANIFEST_PATH": "*", + "PROFILE": 1, + "CRATES": "*", + "FEATURES": "*" + } + } + } +} diff --git a/DawPlugin/.gitignore b/DawPlugin/.gitignore new file mode 100644 index 000000000..b8262d117 --- /dev/null +++ b/DawPlugin/.gitignore @@ -0,0 +1,25 @@ +# Created by https://www.toptal.com/developers/gitignore/api/cmake +# Edit at https://www.toptal.com/developers/gitignore?templates=cmake + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +# End of https://www.toptal.com/developers/gitignore/api/cmake + +build/ +!deps/noto_sans/.gitkeep +deps/noto_sans/* diff --git a/DawPlugin/CMakeLists.txt b/DawPlugin/CMakeLists.txt new file mode 100644 index 000000000..c7a71a41f --- /dev/null +++ b/DawPlugin/CMakeLists.txt @@ -0,0 +1,90 @@ +cmake_minimum_required(VERSION 3.24) + +set(DPF_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/dpf") + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(PROJECT_NAME openutau_daw_plugin_debug) +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(PROJECT_NAME openutau_daw_plugin) +elseif(NOT CMAKE_BUILD_TYPE) + message(FATAL_ERROR "Build type not set. Please set CMAKE_BUILD_TYPE to either Debug or Release.") +else() + message(FATAL_ERROR "Unknown build type: ${CMAKE_BUILD_TYPE}") +endif() + +set(NOTO_SANS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/noto_sans") +if(NOT EXISTS "${NOTO_SANS_DIR}/otf") + file( + DOWNLOAD + "https://github.com/notofonts/noto-cjk/releases/download/Sans2.004/05_NotoSansCJK-SubsetOTF.zip" + "${NOTO_SANS_DIR}/05_NotoSansCJK-SubsetOTF.zip") + file(ARCHIVE_EXTRACT INPUT "${NOTO_SANS_DIR}/05_NotoSansCJK-SubsetOTF.zip" + DESTINATION "${NOTO_SANS_DIR}/otf") +endif() +if(NOT EXISTS "${NOTO_SANS_DIR}/noto_sans.hpp") + execute_process( + COMMAND xxd -i "${NOTO_SANS_DIR}/otf/SubsetOTF/JP/NotoSansJP-Regular.otf" + "${NOTO_SANS_DIR}/noto_sans.raw.hpp") + file(READ "${NOTO_SANS_DIR}/noto_sans.raw.hpp" NOTO_SANS_HPP) + string( + REGEX + REPLACE "unsigned char .+\\[\\]" "const unsigned char notoSansJpRegular[]" + NOTO_SANS_HPP "${NOTO_SANS_HPP}") + string( + REGEX + REPLACE "unsigned int .+_len" "const unsigned int notoSansJpRegularLen" + NOTO_SANS_HPP "${NOTO_SANS_HPP}") + file(WRITE "${NOTO_SANS_DIR}/noto_sans.hpp" "${NOTO_SANS_HPP}") +endif() + +include(./deps/dpf/cmake/DPF-plugin.cmake) +project(${PROJECT_NAME}) + +# MSVC only: Use UTF-8 code page, use C++20, Make asio use Windows 10 APIs, Enable IME +if(MSVC) + add_compile_options("/utf-8") + add_compile_options("/std:c++20") + add_definitions(-D_WIN32_WINNT=0x0A00) + add_definitions(-DIMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DDEBUG) + add_definitions(-DDPF_DEBUG) +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + # nothing atm +endif() + +# Disable DGL's default font +add_definitions(-DDGL_NO_SHARED_RESOURCES=1) + +dpf_add_plugin( + ${PROJECT_NAME} + TARGETS vst3 au + UI_TYPE opengl + FILES_COMMON src/common.cpp + FILES_DSP src/plugin.cpp + FILES_UI src/ui.cpp deps/dpf_widgets/opengl/DearImGui.cpp) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20) +set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20) + +# Including other libraries before DPF causes "find_library" to fail with +# infinite recursion, so we include it after DPF +set(ZSTD_BUILD_STATIC ON) +set(ZSTD_BUILD_SHARED OFF) +add_subdirectory(deps/zstd/build/cmake) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC "src" + "deps" + "deps/dpf/dgl" + "deps/dpf/distrho" + "deps/asio/asio/include" + "deps/dpf_widgets" + "deps/dpf_widgets/opengl" + "deps/uuid-v4" + "deps/yamc/include" + "deps/zstd/lib") + +target_link_libraries(${PROJECT_NAME} PRIVATE libzstd_static) diff --git a/DawPlugin/CMakeSettings.json b/DawPlugin/CMakeSettings.json new file mode 100644 index 000000000..5a03c8d7c --- /dev/null +++ b/DawPlugin/CMakeSettings.json @@ -0,0 +1,24 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Visual Studio 17 2022 Win64", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "-DCMAKE_BUILD_TYPE=Debug", + "ctestCommandArgs": "" + }, + { + "name": "x64-Release", + "generator": "Visual Studio 17 2022 Win64", + "configurationType": "Release", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "-DCMAKE_BUILD_TYPE=Release", + "ctestCommandArgs": "" + } + ] +} diff --git a/DawPlugin/IPC.md b/DawPlugin/IPC.md new file mode 100644 index 000000000..4c8ab0bc5 --- /dev/null +++ b/DawPlugin/IPC.md @@ -0,0 +1,63 @@ +# Inter-Process Communication (IPC) between OpenUtau and DAW Plugin + +This document outlines the inter-process communication (IPC) mechanism used for integration between the OpenUtau main application and its DAW plugin. + +## Overview + +The communication between OpenUtau and the DAW plugin is established using **TCP sockets** over the loopback interface (`127.0.0.1`, localhost). All messages exchanged are serialized and deserialized using **JSON**. + +## Key Components + +The IPC mechanism is primarily managed by classes within the `OpenUtau.Core.DawIntegration` namespace. + +## IPC Workflow Summary + +1. **DAW Plugin Initialization**: A DAW plugin, upon starting, acts as a TCP server and typically creates a `.json` file in the designated temporary directory (`OpenUtau/PluginServers`) containing its listening port and name. +2. **OpenUtau Server Discovery**: OpenUtau's `DawServerFinder` periodically scans this temporary directory to discover available DAW plugin servers. +3. **Connection Establishment**: When a DAW plugin is selected or detected, OpenUtau (via `DawManager` and `DawClient`) initiates a TCP connection to the DAW plugin's exposed port on `127.0.0.1`. +4. **Initial Handshake**: OpenUtau sends an `init` request, and loads the current USTX project data from the DAW plugin. +5. **Continuous Synchronization**: + - OpenUtau continuously monitors changes in its project (USTX, tracks, parts). + - Using debounced notifications (`updateUstx`, `updateTracks`), OpenUtau pushes relevant updates to the DAW plugin. + - For audio synchronization, OpenUtau sends `updatePartLayout` with hashes of audio parts. The DAW plugin responds with hashes of any missing audio. + - OpenUtau then renders and sends the missing audio data via `UpdateAudioNotification` (compressed and encoded). +6. **DAW to OpenUtau Communication**: The DAW plugin can send notifications (`ping`) or requests back to OpenUtau, which are handled by registered listeners in `DawClient`. + +## OpenUtau to DAW Plugin Messages + +There are 4 main types of messages sent from OpenUtau to the DAW plugin: + +- **OpenUtau to DAW Requests**: Sent by OpenUtau to the DAW plugin, expecting a response. +- **OpenUtau to DAW Notifications**: Sent by OpenUtau to the DAW plugin, not expecting a response. +- **DAW Plugin to OpenUtau Requests**: Sent by the DAW plugin to OpenUtau, expecting a response. Currently not used. +- **DAW Plugin to OpenUtau Notifications**: Sent by the DAW plugin to OpenUtau, not expecting a response. + +The following messages are sent from OpenUtau to the DAW plugin: + +- `init` (Request): + - Initializes the connection between OpenUtau and the DAW plugin. Also retrives the current USTX project data saved in plugin. + - **Request**: None + - **Response**: `{ "ustx": }` +- `updateUstx` (Notification): + - Notifies the DAW plugin about changes to the entire USTX project in OpenUtau. + - **Request**: `{ "ustx": }` + - **Response**: None +- `updateTracks` (Notification): + - Notifies the DAW plugin about changes to track properties (e.g., name, volume, pan) in OpenUtau. + - **Request**: `{ "tracks": [ { "name": , "volume": , "pan": } ] }` + - **Response**: None +- `updatePartLayout` (Request): + - Synchronizes part layout information (e.g., track number, start/end times, audio hashes) from OpenUtau to the DAW. + - **Request**: `{ "parts": [ { "trackNo": , "startMs": , "endMs": , "audioHash":