### Build and Install Elkcpp Source: https://github.com/elk-audio/elkcpp/blob/master/README.md After setting up dependencies, build the library using make and then install it. ```console $ make $ make install ``` -------------------------------- ### Install Elkcpp Dependencies Source: https://github.com/elk-audio/elkcpp/blob/master/README.md Clone the repository and initialize submodules, then bootstrap vcpkg for dependency management. ```console $ git submodule update --init --recursive $ ./third-party/vcpkg/bootstrap-vcpkg.sh $ mkdir build && cd build $ cmake -DCMAKE_TOOLCHAIN_FILE=../third-party/vcpkg/scripts/buildsystems/vcpkg.cmake .. ``` -------------------------------- ### Setup Notifications and Subscriptions Source: https://context7.com/elk-audio/elkcpp/llms.txt Initializes notification subscriptions for transport, parameters, CPU timing, tracks, and processors. Requires enabling CPU timings separately. ```cpp void setup_notifications(sushi_controller::SushiController* controller) { auto* notifications = controller->notification_controller(); auto* timing = controller->timing_controller(); // Subscribe to transport changes notifications->subscribe_to_transport_changes(on_transport_change); // Subscribe to parameter updates (empty blocklist = all parameters) std::vector> blocklist; notifications->subscribe_to_parameter_updates(on_parameter_change, blocklist); // Subscribe to CPU timing updates (requires enabling timings) timing->set_timings_enabled(true); notifications->subscribe_to_engine_cpu_timing_updates(on_timing_update); // Subscribe to track and processor changes notifications->subscribe_to_track_changes([](sushi_controller::TrackUpdate update) { std::cout << "Track " << (update.action == sushi_controller::TrackUpdate::Action::TRACK_ADDED ? "added" : "deleted") << " - ID: " << update.track_id << std::endl; }); notifications->subscribe_to_processor_changes(on_processor_change); } ``` -------------------------------- ### Manage Audio Graph Operations in C++ Source: https://context7.com/elk-audio/elkcpp/llms.txt Demonstrates various operations on the audio graph controller, including getting tracks, processors, creating tracks, loading plugins, and managing bypass states. Ensure the SushiController is properly initialized before use. ```cpp #include "sushi_client.h" #include void manage_audio_graph(sushi_controller::SushiController* controller) { auto* graph = controller->audio_graph_controller(); sushi_controller::ControlStatus status; // Get all tracks std::vector tracks; std::tie(status, tracks) = graph->get_all_tracks(); for (const auto& track : tracks) { std::cout << "Track: " << track.name << " (ID: " << track.id << ", Channels: " << track.channels << ")" << std::endl; } // Get track ID by name int track_id; std::tie(status, track_id) = graph->get_track_id("main"); if (status != sushi_controller::ControlStatus::OK) { std::cerr << "Track 'main' not found" << std::endl; return; } // Get processors on a track std::vector processors; std::tie(status, processors) = graph->get_track_processors(track_id); for (const auto& proc : processors) { std::cout << " Processor: " << proc.name << " (ID: " << proc.id << ", Params: " << proc.parameter_count << ")" << std::endl; } // Create a new stereo track sushi_controller::ControlResponse response; response = graph->create_track("synth_track", 2, std::nullopt); if (response.status == sushi_controller::ControlStatus::OK) { std::cout << "Created track with ID: " << response.id << std::endl; } // Load a VST3 plugin on the track response = graph->create_processor_on_track( "mda JX10", // Name for the processor instance "mda JX10", // UID (for VST3, use plugin name) "mda-vst3.vst3", // Path to plugin sushi_controller::PluginType::VST3X, // Plugin type track_id, // Target track ID 0, // Before processor (ignored if add_to_back is true) true // Add to back of chain ); // Load an internal Sushi plugin response = graph->create_processor_on_track( "step_sequencer", "sushi.testing.step_sequencer", // Internal plugin UID "", // No path needed for internal plugins sushi_controller::PluginType::INTERNAL, track_id, 0, true ); // Get processor bypass state int processor_id; std::tie(status, processor_id) = graph->get_processor_id("mda JX10"); bool bypassed; std::tie(status, bypassed) = graph->get_processor_bypass_state(processor_id); std::cout << "Processor bypassed: " << (bypassed ? "yes" : "no") << std::endl; // Toggle bypass response = graph->set_processor_bypass_state(processor_id, !bypassed); // Delete a processor from a track response = graph->delete_processor_from_track(processor_id, track_id); // Delete an entire track response = graph->delete_track(track_id); } ``` -------------------------------- ### Manage Audio Routing Connections Source: https://context7.com/elk-audio/elkcpp/llms.txt Control audio signal flow by managing connections between engine inputs/outputs and tracks. Supports getting, connecting, and disconnecting audio channels. ```cpp #include "sushi_client.h" #include void manage_audio_routing(sushi_controller::SushiController* controller) { auto* routing = controller->audio_routing_controller(); sushi_controller::ControlStatus status; // Get all input connections std::vector inputs; std::tie(status, inputs) = routing->get_all_input_connections(); for (const auto& conn : inputs) { std::cout << "Input: Engine Ch " << conn.engine_channel << " -> Track " << conn.track_id << " Ch " << conn.track_channel << std::endl; } // Get all output connections std::vector outputs; std::tie(status, outputs) = routing->get_all_output_connections(); for (const auto& conn : outputs) { std::cout << "Output: Track " << conn.track_id << " Ch " << conn.track_channel << " -> Engine Ch " << conn.engine_channel << std::endl; } int track_id = 0; // Connect engine input channel 0 to track channel 0 auto response = routing->connect_input_channel_to_track(track_id, 0, 0); // Connect engine input channel 1 to track channel 1 response = routing->connect_input_channel_to_track(track_id, 1, 1); // Connect track output to engine outputs (stereo) response = routing->connect_output_channel_from_track(track_id, 0, 0); response = routing->connect_output_channel_from_track(track_id, 1, 1); // Using AudioConnection struct sushi_controller::AudioConnection conn{track_id, 0, 2}; // track_id, track_channel, engine_channel response = routing->connect_input_channel_to_track(conn); // Disconnect a specific connection response = routing->disconnect_input(track_id, 0, 0); response = routing->disconnect_output(track_id, 0, 0); // Disconnect all connections from a track response = routing->disconnect_all_inputs_from_track(track_id); response = routing->disconnect_all_outputs_from_track(track_id); } ``` -------------------------------- ### Create and Initialize Sushi Controller Source: https://context7.com/elk-audio/elkcpp/llms.txt Demonstrates how to create a SushiController instance, connect to a Sushi host (default or specific address), and verify API version compatibility. Accesses various specialized controllers for further interaction. ```cpp #include "sushi_client.h" int main() { // Connect to Sushi at default address (localhost:51051) std::shared_ptr controller = sushi_controller::CreateSushiController(); // Or connect to a specific address std::shared_ptr remote_controller = sushi_controller::CreateSushiController("192.168.1.100:51051"); // Verify API version compatibility std::string sushi_api_version = controller->system_controller()->get_sushi_api_version().second; std::string elkcpp_api_version = sushi_controller::get_elkcpp_app_version(); if (sushi_api_version != elkcpp_api_version) { std::cerr << "API Version Mismatch! Sushi: " << sushi_api_version << ", ElkCpp: " << elkcpp_api_version << std::endl; return -1; } // Access specialized controllers auto* system = controller->system_controller(); auto* transport = controller->transport_controller(); auto* audio_graph = controller->audio_graph_controller(); auto* parameter = controller->parameter_controller(); auto* keyboard = controller->keyboard_controller(); auto* notifications = controller->notification_controller(); return 0; } ``` -------------------------------- ### Query System Information with System Controller Source: https://context7.com/elk-audio/elkcpp/llms.txt Shows how to use the System Controller to retrieve version information, build details (version, commit hash, build date, buffer size, options), and audio channel counts from the Sushi instance. ```cpp #include "sushi_client.h" #include void query_system_info(sushi_controller::SushiController* controller) { auto* system = controller->system_controller(); sushi_controller::ControlStatus status; // Get Sushi version std::string version; std::tie(status, version) = system->get_sushi_version(); if (status == sushi_controller::ControlStatus::OK) { std::cout << "Sushi Version: " << version << std::endl; } // Get build information sushi_controller::BuildInfo build_info; std::tie(status, build_info) = system->get_build_info(); if (status == sushi_controller::ControlStatus::OK) { std::cout << "Build Version: " << build_info.version << std::endl; std::cout << "Commit Hash: " << build_info.commit_hash << std::endl; std::cout << "Build Date: " << build_info.build_date << std::endl; std::cout << "Audio Buffer Size: " << build_info.audio_buffer_size << std::endl; std::cout << "Build Options: "; for (const auto& opt : build_info.build_options) { std::cout << opt << " "; } std::cout << std::endl; } // Get audio channel counts int input_channels, output_channels; std::tie(status, input_channels) = system->get_input_audio_channel_count(); std::tie(status, output_channels) = system->get_output_audio_channel_count(); std::cout << "Audio Channels - Input: " << input_channels << ", Output: " << output_channels << std::endl; } ``` -------------------------------- ### Automating Plugin Parameters with ParameterController Source: https://context7.com/elk-audio/elkcpp/llms.txt Use this to discover, read, and write plugin parameters. Requires a valid SushiController instance. ```cpp #include "sushi_client.h" #include void control_parameters(sushi_controller::SushiController* controller) { auto* graph = controller->audio_graph_controller(); auto* params = controller->parameter_controller(); sushi_controller::ControlStatus status; // Get processor ID int processor_id; std::tie(status, processor_id) = graph->get_processor_id("mda JX10"); if (status != sushi_controller::ControlStatus::OK) { std::cerr << "Processor not found" << std::endl; return; } // Get all parameters for a processor std::vector parameters; std::tie(status, parameters) = params->get_processor_parameters(processor_id); for (const auto& param : parameters) { std::cout << "Parameter: " << param.name << " (ID: " << param.id << ", Type: " << static_cast(param.type) << ", Range: " << param.min_domain_value << "-" << param.max_domain_value << " " << param.unit << ")" << std::endl; } // Get parameter ID by name int param_id; std::tie(status, param_id) = params->get_parameter_id(processor_id, "VCF Freq"); if (status != sushi_controller::ControlStatus::OK) { std::cerr << "Parameter 'VCF Freq' not found" << std::endl; return; } // Get current parameter value (normalized 0-1) float value; std::tie(status, value) = params->get_parameter_value(processor_id, param_id); std::cout << "VCF Freq normalized value: " << value << std::endl; // Get parameter value in domain (actual units) float domain_value; std::tie(status, domain_value) = params->get_parameter_value_in_domain(processor_id, param_id); std::cout << "VCF Freq domain value: " << domain_value << std::endl; // Get parameter value as formatted string std::string formatted; std::tie(status, formatted) = params->get_parameter_value_as_string(processor_id, param_id); std::cout << "VCF Freq formatted: " << formatted << std::endl; // Set parameter value (normalized 0-1) status = params->set_parameter_value(processor_id, param_id, 0.75f); if (status == sushi_controller::ControlStatus::OK) { std::cout << "Set VCF Freq to 0.75" << std::endl; } // Smoothly automate a parameter for (float v = 0.0f; v <= 1.0f; v += 0.01f) { params->set_parameter_value(processor_id, param_id, v); usleep(10000); // 10ms delay } } ``` -------------------------------- ### Manage Plugin Presets with Program Controller Source: https://context7.com/elk-audio/elkcpp/llms.txt Queries and selects programs (presets) on processors. Retrieves lists of available programs, current program details, and allows switching to a different program. ```cpp #include "sushi_client.h" #include void manage_programs(sushi_controller::SushiController* controller) { auto* programs = controller->program_controller(); auto* graph = controller->audio_graph_controller(); sushi_controller::ControlStatus status; // Get processor ID int processor_id; std::tie(status, processor_id) = graph->get_processor_id("mda JX10"); // Get list of available programs std::vector program_names; std::tie(status, program_names) = programs->get_processor_programs(processor_id); std::cout << "Available programs:" << std::endl; int idx = 0; for (const auto& name : program_names) { std::cout << " " << idx++ << ": " << name << std::endl; } // Get current program int current_program; std::tie(status, current_program) = programs->get_processor_current_program(processor_id); std::cout << "Current program ID: " << current_program << std::endl; // Get current program name std::string current_name; std::tie(status, current_name) = programs->get_processor_current_program_name(processor_id); std::cout << "Current program name: " << current_name << std::endl; // Get name of a specific program std::string prog_name; std::tie(status, prog_name) = programs->get_processor_program_name(processor_id, 5); std::cout << "Program 5 name: " << prog_name << std::endl; // Change to a different program auto response = programs->set_processor_program(processor_id, 3); if (response.status == sushi_controller::ControlStatus::OK) { std::cout << "Switched to program 3" << std::endl; } } ``` -------------------------------- ### Manage MIDI Connections with C++ Source: https://context7.com/elk-audio/elkcpp/llms.txt Configure MIDI routing, including keyboard input, CC mappings, and program change assignments. Ensure the SushiController is initialized before use. ```cpp #include "sushi_client.h" #include void manage_midi_routing(sushi_controller::SushiController* controller) { auto* midi = controller->midi_controller(); sushi_controller::ControlStatus status; // Get number of MIDI ports int input_ports, output_ports; std::tie(status, input_ports) = midi->get_input_ports(); std::tie(status, output_ports) = midi->get_output_ports(); std::cout << "MIDI Ports - Input: " << input_ports << ", Output: " << output_ports << std::endl; // Connect MIDI keyboard input to a track sushi_controller::MidiKbdConnection kbd_conn; kbd_conn.track_id = 0; kbd_conn.port = 0; kbd_conn.channel = sushi_controller::MidiChannel::MIDI_CH_OMNI; // Receive all channels kbd_conn.raw_midi = false; auto response = midi->connect_kbd_input_to_track(kbd_conn); // Connect MIDI CC to a parameter sushi_controller::MidiCCConnection cc_conn; cc_conn.processor_id = 1; cc_conn.parameter_id = 5; // Parameter to control cc_conn.port = 0; cc_conn.channel = sushi_controller::MidiChannel::MIDI_CH_1; cc_conn.cc_number = 74; // CC#74 (Filter cutoff) cc_conn.min_range = 0.0f; cc_conn.max_range = 1.0f; cc_conn.relative_mode = false; response = midi->connect_cc_to_parameter(cc_conn); // Connect MIDI Program Change to a processor sushi_controller::MidiPCConnection pc_conn; pc_conn.processor_id = 1; pc_conn.port = 0; pc_conn.channel = sushi_controller::MidiChannel::MIDI_CH_1; response = midi->connect_pc_to_processor(pc_conn); // Get all CC connections std::vector cc_connections; std::tie(status, cc_connections) = midi->get_all_cc_input_connections(); for (const auto& cc : cc_connections) { std::cout << "CC#" << cc.cc_number << " -> Processor " << cc.processor_id << " Param " << cc.parameter_id << std::endl; } // Enable MIDI clock output on port 0 midi->set_midi_clock_output_enabled(true, 0); // Disconnect CC connection response = midi->disconnect_cc(cc_conn); // Disconnect all CC from a processor response = midi->disconnect_all_cc_from_processor(1); } ``` -------------------------------- ### Bootstrap vcpkg Source: https://github.com/elk-audio/elkcpp/blob/master/README.md Bootstrap vcpkg within your project directory after adding it as a submodule. ```console $ ./third-party/vcpkg/bootstrap-vcpkg.sh ``` -------------------------------- ### Define Test Source Files Source: https://github.com/elk-audio/elkcpp/blob/master/test/CMakeLists.txt Lists all integration and unit test source files to be compiled. ```cmake SET(TEST_FILES unittests/audio_graph_integration_test.cpp unittests/audio_routing_integration_test.cpp unittests/cv_gate_integration_test.cpp unittests/keyboard_integration_test.cpp unittests/midi_integration_test.cpp unittests/notification_integration_test.cpp unittests/osc_integration_test.cpp unittests/parameter_integration_test.cpp unittests/program_integration_test.cpp unittests/system_integration_test.cpp unittests/timing_integration_test.cpp unittests/transport_integration_test.cpp unittests/session_integation_test.cpp unittests/sync_helper_test.cpp) ``` -------------------------------- ### Control Transport Settings in C++ Source: https://context7.com/elk-audio/elkcpp/llms.txt Demonstrates how to access the transport controller to modify tempo, time signature, sync mode, and playback state. Requires a valid SushiController instance. ```cpp #include "sushi_client.h" #include void control_transport(sushi_controller::SushiController* controller) { auto* transport = controller->transport_controller(); sushi_controller::ControlStatus status; // Get current samplerate float samplerate; std::tie(status, samplerate) = transport->get_samplerate(); std::cout << "Sample Rate: " << samplerate << " Hz" << std::endl; // Set tempo to 120 BPM status = transport->set_tempo(120.0f); if (status == sushi_controller::ControlStatus::OK) { std::cout << "Tempo set to 120 BPM" << std::endl; } // Get current tempo float tempo; std::tie(status, tempo) = transport->get_tempo(); std::cout << "Current Tempo: " << tempo << " BPM" << std::endl; // Set time signature to 4/4 sushi_controller::TimeSignature time_sig{4, 4}; status = transport->set_time_signature(time_sig); // Set sync mode (INTERNAL, MIDI, or LINK) status = transport->set_sync_mode(sushi_controller::SyncMode::INTERNAL); // Start playback status = transport->set_playing_mode(sushi_controller::PlayingMode::PLAYING); std::cout << "Playback started" << std::endl; // Check playing mode sushi_controller::PlayingMode mode; std::tie(status, mode) = transport->get_playing_mode(); if (mode == sushi_controller::PlayingMode::PLAYING) { std::cout << "Currently playing" << std::endl; } // Stop playback status = transport->set_playing_mode(sushi_controller::PlayingMode::STOPPED); std::cout << "Playback stopped" << std::endl; } ``` -------------------------------- ### Create Sushi Controller Instance Source: https://github.com/elk-audio/elkcpp/blob/master/README.md Instantiate a sushi controller using the provided factory function. This returns a shared pointer to the control interface. ```cpp std::shared_ptr controller = sushi_controller::CreateSushiController(); ``` -------------------------------- ### Configure Google Test Framework Source: https://github.com/elk-audio/elkcpp/blob/master/test/CMakeLists.txt Initializes the Google Test subdirectory and enables testing capabilities. ```cmake add_subdirectory(googletest EXCLUDE_FROM_ALL) enable_testing() ``` -------------------------------- ### Add vcpkg Submodule Source: https://github.com/elk-audio/elkcpp/blob/master/README.md If gRPC is not available, add vcpkg as a submodule to your project. ```console $ git submodule add https://github.com/Microsoft/vcpkg.git third-party/vcpkg ``` -------------------------------- ### Save and Restore Session State with C++ Source: https://context7.com/elk-audio/elkcpp/llms.txt Persist and load the entire Sushi session, including tracks, plugins, and parameter values. Ensure the session file exists for restoration. ```cpp #include "sushi_client.h" #include #include void manage_sessions(sushi_controller::SushiController* controller) { auto session = sushi_controller::CreateSessionController(); sushi_controller::ControlStatus status; // Save current session to binary format std::string session_data; std::tie(status, session_data) = session->save_binary_session(); if (status == sushi_controller::ControlStatus::OK) { // Write to file std::ofstream file("session.bin", std::ios::binary); file.write(session_data.data(), session_data.size()); file.close(); std::cout << "Session saved (" << session_data.size() << " bytes)" << std::endl; } // Restore session from saved data std::ifstream infile("session.bin", std::ios::binary); std::string loaded_data((std::istreambuf_iterator(infile)), std::istreambuf_iterator()); infile.close(); auto response = session->restore_binary_session(loaded_data); if (response.status == sushi_controller::ControlStatus::OK) { std::cout << "Session restored successfully" << std::endl; } } ``` -------------------------------- ### Include Sushi Client Header Source: https://github.com/elk-audio/elkcpp/blob/master/README.md Include the necessary header file in your C++ source file to use the sushi controller. ```cpp #include "sushi_client.h" ``` -------------------------------- ### Define Test Dependencies and Build Targets Source: https://github.com/elk-audio/elkcpp/blob/master/test/CMakeLists.txt Configures linked libraries, include directories, compiler features, and test execution targets. ```cmake set(ELKCPP_TEST_LINK_LIBRARIES elkcpp gtest gtest_main ) set(ELKCPP_TEST_INCLUDE_DIRS ${SUSHI_GRPC_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/client ${PROJECT_SOURCE_DIR}/test/unittests ${PROJECT_SOURCE_DIR}/test/googletest/include ) add_executable(unit_tests ${TEST_FILES}) target_link_libraries(unit_tests "${ELKCPP_TEST_LINK_LIBRARIES}") target_include_directories(unit_tests PRIVATE ${ELKCPP_TEST_INCLUDE_DIRS}) target_compile_features(unit_tests PRIVATE cxx_std_20) target_compile_options(unit_tests PRIVATE -Wall -Wextra) add_test(unit_tests unit_tests) # custom target for running the tests add_custom_target(run_tests ALL COMMAND "./unit_tests") add_dependencies(run_tests unit_tests) ``` -------------------------------- ### Synchronously Load Plugins with SyncHelper Source: https://context7.com/elk-audio/elkcpp/llms.txt Use SyncHelper to convert asynchronous plugin loading operations into synchronous calls. It supports default or custom timeouts and allows waiting for individual or all operations. ```cpp #include "sushi_client.h" #include "sync_helper.h" #include void load_plugins_synchronously(sushi_controller::SushiController* controller) { auto* graph = controller->audio_graph_controller(); auto* notifications = controller->notification_controller(); // Create SyncHelper with default 5-second timeout sushi_controller::SyncHelper helper(notifications); // Or with custom timeout sushi_controller::SyncHelper helper_custom( notifications, std::chrono::milliseconds(10000) // 10 second timeout ); int track_id = 0; // Synchronously load a plugin (blocks until complete or timeout) auto response = graph->create_processor_on_track( "synth", "mda JX10", "mda-vst3.vst3", sushi_controller::PluginType::VST3X, track_id, 0, true ); sushi_controller::ControlStatus status = helper.wait_for_response(response); if (status == sushi_controller::ControlStatus::OK) { std::cout << "Plugin loaded successfully" << std::endl; } else if (status == sushi_controller::ControlStatus::TIMED_OUT) { std::cerr << "Plugin loading timed out" << std::endl; } else { std::cerr << "Plugin loading failed with status: " << static_cast(status) << std::endl; } // Load multiple plugins and wait for all auto resp1 = graph->create_processor_on_track("eq", "sushi.testing.equalizer", "", sushi_controller::PluginType::INTERNAL, track_id, 0, true); int id1 = helper.add_to_responselist(resp1); auto resp2 = graph->create_processor_on_track("delay", "mda DubDelay", "mda-vst3.vst3", sushi_controller::PluginType::VST3X, track_id, 0, true); int id2 = helper.add_to_responselist(resp2); // Wait for all queued operations to complete helper.wait_for_all(); // Check individual results auto status1 = helper.status_for_id(id1); auto status2 = helper.status_for_id(id2); if (status1 && *status1 == sushi_controller::ControlStatus::OK) { std::cout << "EQ loaded successfully" << std::endl; } if (status2 && *status2 == sushi_controller::ControlStatus::OK) { std::cout << "Delay loaded successfully" << std::endl; } } ``` -------------------------------- ### Set Include Directories Source: https://github.com/elk-audio/elkcpp/blob/master/test/CMakeLists.txt Defines the include paths required for the test build. ```cmake set(INCLUDE_DIRS ${GRPC_WRAPPER_PUBLIC_HEADER_DIR} ${PROJECT_SOURCE_DIR}/test/googletest/include ${PROJECT_SOURCE_DIR}/src/client) ``` -------------------------------- ### Subscribe to CPU Timing Updates Source: https://context7.com/elk-audio/elkcpp/llms.txt Callback function for receiving CPU timing updates. It logs the average, minimum, and maximum CPU load percentages. ```cpp // Callback for CPU timing updates void on_timing_update(const sushi_controller::CpuTimings& timings) { std::cout << "CPU Load - Avg: " << timings.main.avg << "%, Min: " << timings.main.min << "%, Max: " << timings.main.max << "%" << std::endl; } ``` -------------------------------- ### Sending MIDI Messages with KeyboardController Source: https://context7.com/elk-audio/elkcpp/llms.txt Use this to send notes, pitch bend, and modulation data to tracks. Requires a valid SushiController instance. ```cpp #include "sushi_client.h" #include void control_keyboard(sushi_controller::SushiController* controller) { auto* keyboard = controller->keyboard_controller(); int track_id = 0; // Target track ID int channel = 0; // MIDI channel (0-15) // Send note on (note 60 = middle C, velocity 0.0-1.0) keyboard->send_note_on(track_id, channel, 60, 0.8f); // Play a chord (C major) keyboard->send_note_on(track_id, channel, 60, 1.0f); // C keyboard->send_note_on(track_id, channel, 64, 1.0f); // E keyboard->send_note_on(track_id, channel, 67, 1.0f); // G usleep(1000000); // Hold for 1 second // Send note off keyboard->send_note_off(track_id, channel, 60, 0.0f); keyboard->send_note_off(track_id, channel, 64, 0.0f); keyboard->send_note_off(track_id, channel, 67, 0.0f); // Send pitch bend (-1.0 to 1.0, 0.0 is center) for (float bend = 0.0f; bend <= 1.0f; bend += 0.1f) { keyboard->send_pitch_bend(track_id, channel, bend); usleep(50000); } // Send modulation wheel (0.0-1.0) keyboard->send_modulation(track_id, channel, 0.5f); // Send channel aftertouch keyboard->send_aftertouch(track_id, channel, 0.7f); // Send polyphonic aftertouch (per-note pressure) keyboard->send_note_aftertouch(track_id, channel, 60, 0.6f); } ``` -------------------------------- ### Include Elkcpp in CMake Project Source: https://github.com/elk-audio/elkcpp/blob/master/README.md Find the gRPC and elkcpp packages and link the elkcpp library to your target. Ensure gRPC is available, potentially via vcpkg. ```cmake find_package(gRPC REQUIRED) find_package(elkcpp CONFIG REQUIRED) target_link_libraries($YOUR_TARGET elkcpp::elkcpp) ``` -------------------------------- ### Monitor CPU Usage with Timing Controller Source: https://context7.com/elk-audio/elkcpp/llms.txt Enables and retrieves CPU timing measurements for the audio engine, individual threads, tracks, and processors. Resets timing measurements. ```cpp #include "sushi_client.h" #include void monitor_cpu_usage(sushi_controller::SushiController* controller) { auto* timing = controller->timing_controller(); sushi_controller::ControlStatus status; // Enable timing measurements status = timing->set_timings_enabled(true); // Check if timings are enabled bool enabled; std::tie(status, enabled) = timing->get_timings_enabled(); std::cout << "Timings enabled: " << (enabled ? "yes" : "no") << std::endl; // Get engine-wide timings sushi_controller::CpuTimings engine_timings; std::tie(status, engine_timings) = timing->get_engine_timings(); std::cout << "Engine CPU - Avg: " << engine_timings.main.avg << "%, Min: " << engine_timings.main.min << "%, Max: " << engine_timings.main.max << "%" << std::endl; // Per-thread timings int thread_id = 0; for (const auto& t : engine_timings.threads) { std::cout << " Thread " << thread_id++ << " - Avg: " << t.avg << "%, Min: " << t.min << "%, Max: " << t.max << "%" << std::endl; } // Get timings for a specific track int track_id = 0; sushi_controller::Timings track_timings; std::tie(status, track_timings) = timing->get_track_timings(track_id); std::cout << "Track 0 CPU - Avg: " << track_timings.avg << "%, Min: " << track_timings.min << "%, Max: " << track_timings.max << "%" << std::endl; // Get timings for a specific processor int processor_id = 1; sushi_controller::Timings proc_timings; std::tie(status, proc_timings) = timing->get_processor_timings(processor_id); std::cout << "Processor 1 CPU - Avg: " << proc_timings.avg << "%" << std::endl; // Reset timing measurements status = timing->reset_all_timings(); status = timing->reset_track_timings(track_id); status = timing->reset_processor_timings(processor_id); } ``` -------------------------------- ### Configure Project with vcpkg Toolchain Source: https://github.com/elk-audio/elkcpp/blob/master/README.md Pass the vcpkg toolchain file option when configuring your CMake project to use vcpkg dependencies. ```console -DCMAKE_TOOLCHAIN_FILE=../third-party/vcpkg/scripts/buildsystems/vcpkg.cmake ``` -------------------------------- ### Subscribe to Parameter Changes Source: https://context7.com/elk-audio/elkcpp/llms.txt Callback function for handling parameter change notifications. It logs the processor ID, parameter ID, normalized value, and formatted value. ```cpp // Callback for parameter changes void on_parameter_change(int parameter_id, int processor_id, float normalized_value, float domain_value, const std::string& formatted_value) { std::cout << "Parameter changed - Processor: " << processor_id << ", Param: " << parameter_id << ", Value: " << normalized_value << " (" << formatted_value << ")" << std::endl; } ``` -------------------------------- ### Subscribe to Processor Changes Source: https://context7.com/elk-audio/elkcpp/llms.txt Callback function to handle processor addition or deletion events. It logs the processor ID and its parent track ID if added. ```cpp // Callback for processor changes void on_processor_change(sushi_controller::ProcessorUpdate update) { if (update.action == sushi_controller::ProcessorUpdate::Action::PROCESSOR_ADDED) { std::cout << "Processor added - ID: " << update.processor_id << " on Track: " << update.parent_track_id << std::endl; } else { std::cout << "Processor deleted - ID: " << update.processor_id << std::endl; } } ``` -------------------------------- ### Link Executable to Elkcpp Library Source: https://github.com/elk-audio/elkcpp/blob/master/examples/CMakeLists.txt Links an executable target to the elkcpp library. This makes the Elkcpp functionalities available to the executable. ```cmake target_link_libraries(simple-sushi-controller PRIVATE elkcpp) ``` ```cmake target_link_libraries(async-sushi-controller PRIVATE elkcpp) ``` ```cmake target_link_libraries(sushi-control-example PRIVATE elkcpp) ``` -------------------------------- ### Add Executable Target Source: https://github.com/elk-audio/elkcpp/blob/master/examples/CMakeLists.txt Defines an executable target in the build system. Specify the executable name and its source files. ```cmake add_executable(simple-sushi-controller SimpleSushiController.cpp) ``` ```cmake add_executable(async-sushi-controller AsyncSushiController.cpp) ``` ```cmake add_executable(sushi-control-example SushiControlExample.cpp) ``` -------------------------------- ### Subscribe to Transport Changes Source: https://context7.com/elk-audio/elkcpp/llms.txt Callback function to handle transport update events. It processes updates for playing mode, tempo, and time signature. ```cpp #include "sushi_client.h" #include // Callback for transport changes void on_transport_change(sushi_controller::TransportUpdate update, sushi_controller::TransportUpdateType type) { switch (type) { case sushi_controller::TransportUpdateType::PLAYING_MODE: { auto mode = std::get(update); switch (mode) { case sushi_controller::PlayingMode::PLAYING: std::cout << "Transport: Playing" << std::endl; break; case sushi_controller::PlayingMode::STOPPED: std::cout << "Transport: Stopped" << std::endl; break; case sushi_controller::PlayingMode::RECORDING: std::cout << "Transport: Recording" << std::endl; break; } break; } case sushi_controller::TransportUpdateType::TEMPO: { auto tempo = std::get(update); std::cout << "Tempo changed to: " << tempo << " BPM" << std::endl; break; } case sushi_controller::TransportUpdateType::TIME_SIGNATURE: { auto sig = std::get(update); std::cout << "Time signature: " << sig.numerator << "/" << sig.denominator << std::endl; break; } default: break; } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.