mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
convert to workspace, add suil bindings crate
This commit is contained in:
parent
bd6f8ff9bf
commit
dacce119c4
52 changed files with 1994 additions and 116 deletions
1396
Cargo.lock
generated
1396
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
37
Cargo.toml
37
Cargo.toml
|
|
@ -1,34 +1,3 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "tek"
|
resolver = "2"
|
||||||
edition = "2021"
|
members = [ "crates/*" ]
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
jack = "0.10"
|
|
||||||
clap = { version = "4.5.4", features = [ "derive" ] }
|
|
||||||
crossterm = "0.27"
|
|
||||||
ratatui = { version = "0.26.3", features = [ "unstable-widget-ref", "underline-color" ] }
|
|
||||||
backtrace = "0.3.72"
|
|
||||||
microxdg = "0.1.2"
|
|
||||||
toml = "0.8.12"
|
|
||||||
better-panic = "0.3.0"
|
|
||||||
midly = "0.5"
|
|
||||||
|
|
||||||
vst = "0.4.0"
|
|
||||||
#vst3 = "0.1.0"
|
|
||||||
livi = "0.7.4"
|
|
||||||
#atomic_enum = "0.3.0"
|
|
||||||
wavers = "1.4.3"
|
|
||||||
music-math = "0.1.1"
|
|
||||||
atomic_float = "1.0.0"
|
|
||||||
fraction = "0.15.3"
|
|
||||||
rlsf = "0.2.1"
|
|
||||||
r8brain-rs = "0.3.5"
|
|
||||||
clojure-reader = "0.1.0"
|
|
||||||
once_cell = "1.19.0"
|
|
||||||
|
|
||||||
symphonia = { version = "0.5.4", features = [ "all" ] }
|
|
||||||
|
|
||||||
dasp = { version = "0.11.0", features = [ "all" ] }
|
|
||||||
|
|
||||||
rubato = "0.15.0"
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ Ardour, and probably others. The main view consists of three sections:
|
||||||
* [ ] Pattern chain
|
* [ ] Pattern chain
|
||||||
* [ ] Actually sync
|
* [ ] Actually sync
|
||||||
* Chain:
|
* Chain:
|
||||||
|
* [ ] Add device
|
||||||
* [ ] View and connect device ports in chain view
|
* [ ] View and connect device ports in chain view
|
||||||
* [ ] Open LV2 GUI
|
* [ ] Open LV2 GUI
|
||||||
* [ ] Pin favorite FX parameters with `*`
|
* [ ] Pin favorite FX parameters with `*`
|
||||||
|
|
|
||||||
9
crates/suil/Cargo.toml
Normal file
9
crates/suil/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "suil"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
bindgen = "0.69.4"
|
||||||
18
crates/suil/build.rs
Normal file
18
crates/suil/build.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::env::var;
|
||||||
|
use bindgen::{Builder, CargoCallbacks};
|
||||||
|
fn main() {
|
||||||
|
//println!("cargo:rustc-link-lib=lv2");
|
||||||
|
//println!("cargo:rustc-link-lib=suil");
|
||||||
|
let bindings = Builder::default()
|
||||||
|
.header("wrapper.h")
|
||||||
|
.clang_arg("-Ilv2/include/lv2")
|
||||||
|
.clang_arg("-Isuil/include/suil")
|
||||||
|
.parse_callbacks(Box::new(CargoCallbacks::new()))
|
||||||
|
.generate()
|
||||||
|
.expect("Unable to generate bindings");
|
||||||
|
let out_path = PathBuf::from(var("OUT_DIR").unwrap());
|
||||||
|
bindings
|
||||||
|
.write_to_file(out_path.join("bindings.rs"))
|
||||||
|
.expect("Couldn't write bindings!");
|
||||||
|
}
|
||||||
5
crates/suil/src/bound.rs
Normal file
5
crates/suil/src/bound.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||||
72
crates/suil/src/lib.rs
Normal file
72
crates/suil/src/lib.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
pub mod bound;
|
||||||
|
|
||||||
|
pub struct Host(*mut self::bound::SuilHost);
|
||||||
|
|
||||||
|
pub struct Instance(*mut self::bound::SuilInstance);
|
||||||
|
|
||||||
|
pub struct Controller(*mut self::bound::SuilController);
|
||||||
|
|
||||||
|
impl Host {
|
||||||
|
fn new () -> Self {
|
||||||
|
//let write = std::ptr::null();
|
||||||
|
//let index = std::ptr::null();
|
||||||
|
//let subscribe = std::ptr::null();
|
||||||
|
//let unsubscribe = std::ptr::null();
|
||||||
|
Self(unsafe {
|
||||||
|
bound::suil_init(&mut 0, std::ptr::null_mut(), 0);
|
||||||
|
bound::suil_host_new(None, None, None, None)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn set_touch_func (&self) {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
fn instance (
|
||||||
|
&self,
|
||||||
|
controller: &Controller,
|
||||||
|
container_type_uri: &str,
|
||||||
|
plugin_uri: &str,
|
||||||
|
ui_uri: &str,
|
||||||
|
ui_type_uri: &str,
|
||||||
|
ui_bundle_path: &str,
|
||||||
|
ui_binary_path: &str,
|
||||||
|
features: &[*const bound::LV2_Feature],
|
||||||
|
) -> Result<Instance, Box<dyn std::error::Error>> {
|
||||||
|
let container_type_uri = CString::new(container_type_uri)?;
|
||||||
|
let plugin_uri = CString::new(plugin_uri)?;
|
||||||
|
let ui_uri = CString::new(ui_uri)?;
|
||||||
|
let ui_type_uri = CString::new(ui_type_uri)?;
|
||||||
|
let ui_bundle_path = CString::new(ui_bundle_path)?;
|
||||||
|
let ui_binary_path = CString::new(ui_binary_path)?;
|
||||||
|
Ok(Instance(unsafe {
|
||||||
|
bound::suil_instance_new(
|
||||||
|
self.0,
|
||||||
|
*controller.0,
|
||||||
|
container_type_uri.into_raw(),
|
||||||
|
plugin_uri.into_raw(),
|
||||||
|
ui_uri.into_raw(),
|
||||||
|
ui_type_uri.into_raw(),
|
||||||
|
ui_bundle_path.into_raw(),
|
||||||
|
ui_binary_path.into_raw(),
|
||||||
|
features.as_ptr()
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Host {
|
||||||
|
fn drop (&mut self) -> () {
|
||||||
|
unsafe {
|
||||||
|
bound::suil_host_free(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Instance {
|
||||||
|
fn drop (&mut self) -> () {
|
||||||
|
unsafe {
|
||||||
|
bound::suil_instance_free(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
302
crates/suil/wrapper.h
Normal file
302
crates/suil/wrapper.h
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
// Copyright 2011-2017 David Robillard <d@drobilla.net>
|
||||||
|
// SPDX-License-Identifier: ISC
|
||||||
|
|
||||||
|
/// @file suil.h Public API for Suil
|
||||||
|
|
||||||
|
#ifndef SUIL_SUIL_H
|
||||||
|
#define SUIL_SUIL_H
|
||||||
|
|
||||||
|
#include "lv2/core/lv2.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// SUIL_LIB_IMPORT and SUIL_LIB_EXPORT mark the entry points of shared libraries
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define SUIL_LIB_IMPORT __declspec(dllimport)
|
||||||
|
# define SUIL_LIB_EXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
# define SUIL_LIB_IMPORT __attribute__((visibility("default")))
|
||||||
|
# define SUIL_LIB_EXPORT __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SUIL_API exposes symbols in the public API
|
||||||
|
#ifndef SUIL_API
|
||||||
|
# ifdef SUIL_STATIC
|
||||||
|
# define SUIL_API
|
||||||
|
# elif defined(SUIL_INTERNAL)
|
||||||
|
# define SUIL_API SUIL_LIB_EXPORT
|
||||||
|
# else
|
||||||
|
# define SUIL_API SUIL_LIB_IMPORT
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
@defgroup suil Suil C API
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@defgroup suil_callbacks Callbacks
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
UI controller.
|
||||||
|
|
||||||
|
This is an opaque pointer passed by the user which is passed to the various
|
||||||
|
UI control functions (e.g. SuilPortWriteFunc). It is typically used to pass
|
||||||
|
a pointer to some controller object the host uses to communicate with
|
||||||
|
plugins.
|
||||||
|
*/
|
||||||
|
typedef void* SuilController;
|
||||||
|
|
||||||
|
/// Function to write/send a value to a port
|
||||||
|
typedef void (*SuilPortWriteFunc)( //
|
||||||
|
SuilController controller,
|
||||||
|
uint32_t port_index,
|
||||||
|
uint32_t buffer_size,
|
||||||
|
uint32_t protocol,
|
||||||
|
void const* buffer);
|
||||||
|
|
||||||
|
/// Function to return the index for a port by symbol
|
||||||
|
typedef uint32_t (*SuilPortIndexFunc)( //
|
||||||
|
SuilController controller,
|
||||||
|
const char* port_symbol);
|
||||||
|
|
||||||
|
/// Function to subscribe to notifications for a port
|
||||||
|
typedef uint32_t (*SuilPortSubscribeFunc)( //
|
||||||
|
SuilController controller,
|
||||||
|
uint32_t port_index,
|
||||||
|
uint32_t protocol,
|
||||||
|
const LV2_Feature* const* features);
|
||||||
|
|
||||||
|
/// Function to unsubscribe from notifications for a port
|
||||||
|
typedef uint32_t (*SuilPortUnsubscribeFunc)( //
|
||||||
|
SuilController controller,
|
||||||
|
uint32_t port_index,
|
||||||
|
uint32_t protocol,
|
||||||
|
const LV2_Feature* const* features);
|
||||||
|
|
||||||
|
/// Function called when a control is grabbed or released
|
||||||
|
typedef void (*SuilTouchFunc)( //
|
||||||
|
SuilController controller,
|
||||||
|
uint32_t port_index,
|
||||||
|
bool grabbed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@}
|
||||||
|
@defgroup suil_library Library
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Initialization argument
|
||||||
|
typedef enum { SUIL_ARG_NONE } SuilArg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize suil.
|
||||||
|
|
||||||
|
This function should be called as early as possible, before any other GUI
|
||||||
|
toolkit functions. The variable argument list is a sequence of SuilArg keys
|
||||||
|
and corresponding value pairs for passing any necessary platform-specific
|
||||||
|
information. It must be terminated with SUIL_ARG_NONE.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
void
|
||||||
|
suil_init(int* argc, char*** argv, SuilArg key, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if suil can wrap a UI type.
|
||||||
|
|
||||||
|
@param host_type_uri The URI of the desired widget type of the host,
|
||||||
|
corresponding to the `type_uri` parameter of suil_instance_new().
|
||||||
|
|
||||||
|
@param ui_type_uri The URI of the UI widget type.
|
||||||
|
|
||||||
|
@return 0 if wrapping is unsupported, otherwise the quality of the wrapping
|
||||||
|
where 1 is the highest quality (direct native embedding with no wrapping)
|
||||||
|
and increasing values are of a progressively lower quality and/or stability.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
unsigned
|
||||||
|
suil_ui_supported(const char* host_type_uri, const char* ui_type_uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@}
|
||||||
|
@defgroup suil_host Host
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
UI host descriptor.
|
||||||
|
|
||||||
|
This contains the various functions that a plugin UI may use to communicate
|
||||||
|
with the plugin. It is passed to suil_instance_new() to provide these
|
||||||
|
functions to the UI.
|
||||||
|
*/
|
||||||
|
typedef struct SuilHostImpl SuilHost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a new UI host descriptor.
|
||||||
|
|
||||||
|
@param write_func Function to send a value to a plugin port.
|
||||||
|
@param index_func Function to get the index for a port by symbol.
|
||||||
|
@param subscribe_func Function to subscribe to port updates.
|
||||||
|
@param unsubscribe_func Function to unsubscribe from port updates.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
SuilHost*
|
||||||
|
suil_host_new(SuilPortWriteFunc write_func,
|
||||||
|
SuilPortIndexFunc index_func,
|
||||||
|
SuilPortSubscribeFunc subscribe_func,
|
||||||
|
SuilPortUnsubscribeFunc unsubscribe_func);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Set a touch function for a host descriptor.
|
||||||
|
|
||||||
|
Note this function will only be called if the UI supports it.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
void
|
||||||
|
suil_host_set_touch_func(SuilHost* host, SuilTouchFunc touch_func);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Free `host`.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
void
|
||||||
|
suil_host_free(SuilHost* host);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@defgroup suil_instance Instance
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// An instance of an LV2 plugin UI
|
||||||
|
typedef struct SuilInstanceImpl SuilInstance;
|
||||||
|
|
||||||
|
/// Opaque pointer to a UI handle
|
||||||
|
typedef void* SuilHandle;
|
||||||
|
|
||||||
|
/// Opaque pointer to a UI widget
|
||||||
|
typedef void* SuilWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Instantiate a UI for an LV2 plugin.
|
||||||
|
|
||||||
|
This funcion may load a suil module to adapt the UI to the desired toolkit.
|
||||||
|
Suil is configured at compile time to load modules from the appropriate
|
||||||
|
place, but this can be changed at run-time via the environment variable
|
||||||
|
SUIL_MODULE_DIR. This makes it possible to bundle suil with an application.
|
||||||
|
|
||||||
|
Note that some situations (Gtk in Qt, Windows in Gtk) require a parent
|
||||||
|
container to be passed as a feature with URI LV2_UI__parent
|
||||||
|
(http://lv2plug.in/ns/extensions/ui#ui) in order to work correctly. The
|
||||||
|
data must point to a single child container of the host widget set.
|
||||||
|
|
||||||
|
@param host Host descriptor.
|
||||||
|
@param controller Opaque host controller pointer.
|
||||||
|
@param container_type_uri URI of the desired host container widget type.
|
||||||
|
@param plugin_uri URI of the plugin to instantiate this UI for.
|
||||||
|
@param ui_uri URI of the specifically desired UI.
|
||||||
|
@param ui_type_uri URI of the actual UI widget type.
|
||||||
|
@param ui_bundle_path Path of the UI bundle.
|
||||||
|
@param ui_binary_path Path of the UI binary.
|
||||||
|
@param features NULL-terminated array of supported features, or NULL.
|
||||||
|
@return A new UI instance, or NULL if instantiation failed.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
SuilInstance*
|
||||||
|
suil_instance_new(SuilHost* host,
|
||||||
|
SuilController controller,
|
||||||
|
const char* container_type_uri,
|
||||||
|
const char* plugin_uri,
|
||||||
|
const char* ui_uri,
|
||||||
|
const char* ui_type_uri,
|
||||||
|
const char* ui_bundle_path,
|
||||||
|
const char* ui_binary_path,
|
||||||
|
const LV2_Feature* const* features);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Free a plugin UI instance.
|
||||||
|
|
||||||
|
The caller must ensure all references to the UI have been dropped before
|
||||||
|
calling this function (e.g. it has been removed from its parent).
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
void
|
||||||
|
suil_instance_free(SuilInstance* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the handle for a UI instance.
|
||||||
|
|
||||||
|
Returns the handle to the UI instance. The returned handle has opaque type
|
||||||
|
to insulate the Suil API from LV2 extensions, but in pactice it is currently
|
||||||
|
of type `LV2UI_Handle`. This should not normally be needed.
|
||||||
|
|
||||||
|
The returned handle is shared and must not be deleted.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
SuilHandle
|
||||||
|
suil_instance_get_handle(SuilInstance* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the widget for a UI instance.
|
||||||
|
|
||||||
|
Returns an opaque pointer to a widget, the type of which matches the
|
||||||
|
`container_type_uri` parameter of suil_instance_new(). Note this may be a
|
||||||
|
wrapper widget created by Suil, and not necessarily the widget directly
|
||||||
|
implemented by the UI.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
SuilWidget
|
||||||
|
suil_instance_get_widget(SuilInstance* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notify the UI about a change in a plugin port.
|
||||||
|
|
||||||
|
This function can be used to notify the UI about any port change, but in the
|
||||||
|
simplest case is used to set the value of lv2:ControlPort ports. For
|
||||||
|
simplicity, this is a special case where `format` is 0, `buffer_size` is 4,
|
||||||
|
and `buffer` should point to a single float.
|
||||||
|
|
||||||
|
The `buffer` must be valid only for the duration of this call, the UI must
|
||||||
|
not keep a reference to it.
|
||||||
|
|
||||||
|
@param instance UI instance.
|
||||||
|
@param port_index Index of the port which has changed.
|
||||||
|
@param buffer_size Size of `buffer` in bytes.
|
||||||
|
@param format Format of `buffer` (mapped URI, or 0 for float).
|
||||||
|
@param buffer Change data, e.g. the new port value.
|
||||||
|
*/
|
||||||
|
SUIL_API
|
||||||
|
void
|
||||||
|
suil_instance_port_event(SuilInstance* instance,
|
||||||
|
uint32_t port_index,
|
||||||
|
uint32_t buffer_size,
|
||||||
|
uint32_t format,
|
||||||
|
const void* buffer);
|
||||||
|
|
||||||
|
/// Return a data structure defined by some LV2 extension URI
|
||||||
|
SUIL_API
|
||||||
|
const void*
|
||||||
|
suil_instance_extension_data(SuilInstance* instance, const char* uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SUIL_SUIL_H */
|
||||||
39
crates/tek/Cargo.toml
Normal file
39
crates/tek/Cargo.toml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
[package]
|
||||||
|
name = "tek"
|
||||||
|
edition = "2021"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jack = "0.10"
|
||||||
|
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||||
|
crossterm = "0.27"
|
||||||
|
ratatui = { version = "0.26.3", features = [ "unstable-widget-ref", "underline-color" ] }
|
||||||
|
backtrace = "0.3.72"
|
||||||
|
microxdg = "0.1.2"
|
||||||
|
toml = "0.8.12"
|
||||||
|
better-panic = "0.3.0"
|
||||||
|
midly = "0.5"
|
||||||
|
|
||||||
|
vst = "0.4.0"
|
||||||
|
#vst3 = "0.1.0"
|
||||||
|
livi = "0.7.4"
|
||||||
|
#atomic_enum = "0.3.0"
|
||||||
|
wavers = "1.4.3"
|
||||||
|
music-math = "0.1.1"
|
||||||
|
atomic_float = "1.0.0"
|
||||||
|
fraction = "0.15.3"
|
||||||
|
rlsf = "0.2.1"
|
||||||
|
r8brain-rs = "0.3.5"
|
||||||
|
clojure-reader = "0.1.0"
|
||||||
|
once_cell = "1.19.0"
|
||||||
|
|
||||||
|
symphonia = { version = "0.5.4", features = [ "all" ] }
|
||||||
|
|
||||||
|
dasp = { version = "0.11.0", features = [ "all" ] }
|
||||||
|
|
||||||
|
rubato = "0.15.0"
|
||||||
|
|
||||||
|
winit = { version = "0.30.4", features = [ "x11" ] }
|
||||||
|
#winit = { path = "../winit" }
|
||||||
|
|
||||||
|
suil = { path = "../suil" }
|
||||||
|
|
@ -1,15 +1,8 @@
|
||||||
//! Plugin (currently LV2 only; TODO other formats)
|
//! Plugin (currently LV2 only; TODO other formats)
|
||||||
|
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
use ::livi::{
|
|
||||||
World,
|
submod! { lv2 }
|
||||||
Instance,
|
|
||||||
Plugin as LiviPlugin,
|
|
||||||
Features,
|
|
||||||
FeaturesBuilder,
|
|
||||||
Port,
|
|
||||||
event::LV2AtomSequence,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn handle_plugin (state: &mut Plugin, event: &AppEvent) -> Usually<bool> {
|
pub fn handle_plugin (state: &mut Plugin, event: &AppEvent) -> Usually<bool> {
|
||||||
handle_keymap(state, event, KEYMAP_PLUGIN)
|
handle_keymap(state, event, KEYMAP_PLUGIN)
|
||||||
|
|
@ -17,29 +10,29 @@ pub fn handle_plugin (state: &mut Plugin, event: &AppEvent) -> Usually<bool> {
|
||||||
|
|
||||||
/// Key bindings for plugin device.
|
/// Key bindings for plugin device.
|
||||||
pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin {
|
pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin {
|
||||||
[Up, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{
|
[Up, NONE, "/plugin/cursor_up", "move cursor up", |s: &mut Plugin|{
|
||||||
s.selected = s.selected.saturating_sub(1);
|
s.selected = s.selected.saturating_sub(1);
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
[Down, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{
|
[Down, NONE, "/plugin/cursor_down", "move cursor down", |s: &mut Plugin|{
|
||||||
s.selected = (s.selected + 1).min(match &s.plugin {
|
s.selected = (s.selected + 1).min(match &s.plugin {
|
||||||
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
|
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
});
|
});
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
[PageUp, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{
|
[PageUp, NONE, "/plugin/cursor_page_up", "move cursor up", |s: &mut Plugin|{
|
||||||
s.selected = s.selected.saturating_sub(8);
|
s.selected = s.selected.saturating_sub(8);
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
[PageDown, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{
|
[PageDown, NONE, "/plugin/cursor_page_down", "move cursor down", |s: &mut Plugin|{
|
||||||
s.selected = (s.selected + 10).min(match &s.plugin {
|
s.selected = (s.selected + 10).min(match &s.plugin {
|
||||||
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
|
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
});
|
});
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
[Char(','), NONE, "decrement", "decrement value", |s: &mut Plugin|{
|
[Char(','), NONE, "/plugin/decrement", "decrement value", |s: &mut Plugin|{
|
||||||
match s.plugin.as_mut() {
|
match s.plugin.as_mut() {
|
||||||
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
|
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
|
||||||
let index = port_list[s.selected].index;
|
let index = port_list[s.selected].index;
|
||||||
|
|
@ -51,7 +44,7 @@ pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin {
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
[Char('.'), NONE, "increment", "increment value", |s: &mut Plugin|{
|
[Char('.'), NONE, "/plugin/decrement", "increment value", |s: &mut Plugin|{
|
||||||
match s.plugin.as_mut() {
|
match s.plugin.as_mut() {
|
||||||
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
|
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
|
||||||
let index = port_list[s.selected].index;
|
let index = port_list[s.selected].index;
|
||||||
|
|
@ -63,6 +56,16 @@ pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin {
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
|
[Char('g'), NONE, "/plugin/gui_toggle", "toggle plugin UI", |s: &mut Plugin|{
|
||||||
|
match s.plugin {
|
||||||
|
Some(PluginKind::LV2(ref mut plugin)) => {
|
||||||
|
plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?);
|
||||||
|
},
|
||||||
|
Some(_) => unreachable!(),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
|
|
||||||
/// A plugin device.
|
/// A plugin device.
|
||||||
|
|
@ -203,68 +206,6 @@ fn draw_header (state: &Plugin, buf: &mut Buffer, x: u16, y: u16, w: u16) -> Usu
|
||||||
Ok(Rect { x, y, width: w, height: 1 })
|
Ok(Rect { x, y, width: w, height: 1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A LV2 plugin.
|
|
||||||
pub struct LV2Plugin {
|
|
||||||
pub world: World,
|
|
||||||
pub instance: Instance,
|
|
||||||
pub plugin: LiviPlugin,
|
|
||||||
pub features: Arc<Features>,
|
|
||||||
pub port_list: Vec<Port>,
|
|
||||||
pub input_buffer: Vec<LV2AtomSequence>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LV2Plugin {
|
|
||||||
pub fn new (uri: &str) -> Usually<Self> {
|
|
||||||
// Get 1st plugin at URI
|
|
||||||
let world = World::with_load_bundle(&uri);
|
|
||||||
let features = FeaturesBuilder { min_block_length: 1, max_block_length: 65536 };
|
|
||||||
let features = world.build_features(features);
|
|
||||||
let mut plugin = None;
|
|
||||||
for p in world.iter_plugins() {
|
|
||||||
plugin = Some(p);
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let plugin = plugin.unwrap();
|
|
||||||
let err = &format!("init {uri}");
|
|
||||||
|
|
||||||
// Instantiate
|
|
||||||
Ok(Self {
|
|
||||||
world,
|
|
||||||
instance: unsafe {
|
|
||||||
plugin
|
|
||||||
.instantiate(features.clone(), 48000.0)
|
|
||||||
.expect(&err)
|
|
||||||
},
|
|
||||||
port_list: {
|
|
||||||
let mut port_list = vec![];
|
|
||||||
for port in plugin.ports() {
|
|
||||||
port_list.push(port);
|
|
||||||
}
|
|
||||||
port_list
|
|
||||||
},
|
|
||||||
plugin,
|
|
||||||
features,
|
|
||||||
input_buffer: Vec::with_capacity(1024)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plugin {
|
|
||||||
pub fn lv2 (name: &str, path: &str) -> Usually<JackDevice> {
|
|
||||||
let plugin = LV2Plugin::new(path)?;
|
|
||||||
Jack::new(name)?
|
|
||||||
.ports_from_lv2(&plugin.plugin)
|
|
||||||
.run(|ports|Box::new(Self {
|
|
||||||
name: name.into(),
|
|
||||||
path: Some(String::from(path)),
|
|
||||||
plugin: Some(PluginKind::LV2(plugin)),
|
|
||||||
selected: 0,
|
|
||||||
mapping: false,
|
|
||||||
ports
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::vst::host::Host for Plugin {}
|
impl ::vst::host::Host for Plugin {}
|
||||||
|
|
||||||
fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, _path: &str) -> Usually<PluginKind> {
|
fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, _path: &str) -> Usually<PluginKind> {
|
||||||
|
|
@ -277,3 +218,10 @@ fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, _path: &str) -> Usually<PluginKind
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//pub struct LV2PluginUI {
|
||||||
|
//write: (),
|
||||||
|
//controller: (),
|
||||||
|
//widget: (),
|
||||||
|
//features: (),
|
||||||
|
//transfer: (),
|
||||||
|
//}
|
||||||
123
crates/tek/src/devices/plugin/lv2.rs
Normal file
123
crates/tek/src/devices/plugin/lv2.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
use super::*;
|
||||||
|
use ::livi::{
|
||||||
|
World,
|
||||||
|
Instance,
|
||||||
|
Plugin as LiviPlugin,
|
||||||
|
Features,
|
||||||
|
FeaturesBuilder,
|
||||||
|
Port,
|
||||||
|
event::LV2AtomSequence,
|
||||||
|
};
|
||||||
|
use ::winit::{
|
||||||
|
application::ApplicationHandler,
|
||||||
|
event::WindowEvent,
|
||||||
|
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
|
||||||
|
window::{Window, WindowId},
|
||||||
|
platform::x11::EventLoopBuilderExtX11
|
||||||
|
};
|
||||||
|
use ::suil::{self};
|
||||||
|
|
||||||
|
impl Plugin {
|
||||||
|
pub fn lv2 (name: &str, path: &str) -> Usually<JackDevice> {
|
||||||
|
let plugin = LV2Plugin::new(path)?;
|
||||||
|
Jack::new(name)?
|
||||||
|
.ports_from_lv2(&plugin.plugin)
|
||||||
|
.run(|ports|Box::new(Self {
|
||||||
|
name: name.into(),
|
||||||
|
path: Some(String::from(path)),
|
||||||
|
plugin: Some(PluginKind::LV2(plugin)),
|
||||||
|
selected: 0,
|
||||||
|
mapping: false,
|
||||||
|
ports
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A LV2 plugin.
|
||||||
|
pub struct LV2Plugin {
|
||||||
|
pub world: World,
|
||||||
|
pub instance: Instance,
|
||||||
|
pub plugin: LiviPlugin,
|
||||||
|
pub features: Arc<Features>,
|
||||||
|
pub port_list: Vec<Port>,
|
||||||
|
pub input_buffer: Vec<LV2AtomSequence>,
|
||||||
|
pub ui_thread: Option<JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LV2Plugin {
|
||||||
|
pub fn new (uri: &str) -> Usually<Self> {
|
||||||
|
// Get 1st plugin at URI
|
||||||
|
let world = World::with_load_bundle(&uri);
|
||||||
|
let features = FeaturesBuilder { min_block_length: 1, max_block_length: 65536 };
|
||||||
|
let features = world.build_features(features);
|
||||||
|
let mut plugin = None;
|
||||||
|
for p in world.iter_plugins() {
|
||||||
|
plugin = Some(p);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let plugin = plugin.unwrap();
|
||||||
|
let err = &format!("init {uri}");
|
||||||
|
|
||||||
|
// Instantiate
|
||||||
|
Ok(Self {
|
||||||
|
world,
|
||||||
|
instance: unsafe {
|
||||||
|
plugin
|
||||||
|
.instantiate(features.clone(), 48000.0)
|
||||||
|
.expect(&err)
|
||||||
|
},
|
||||||
|
port_list: {
|
||||||
|
let mut port_list = vec![];
|
||||||
|
for port in plugin.ports() {
|
||||||
|
port_list.push(port);
|
||||||
|
}
|
||||||
|
port_list
|
||||||
|
},
|
||||||
|
plugin,
|
||||||
|
features,
|
||||||
|
input_buffer: Vec::with_capacity(1024),
|
||||||
|
ui_thread: None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_lv2_ui (mut ui: LV2PluginUI) -> Usually<JoinHandle<()>> {
|
||||||
|
Ok(spawn(move||{
|
||||||
|
let event_loop = EventLoop::builder().with_x11().with_any_thread(true).build().unwrap();
|
||||||
|
event_loop.set_control_flow(ControlFlow::Wait);
|
||||||
|
event_loop.run_app(&mut ui).unwrap()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A LV2 plugin's X11 UI.
|
||||||
|
pub struct LV2PluginUI {
|
||||||
|
pub window: Option<Window>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LV2PluginUI {
|
||||||
|
pub fn new () -> Usually<Self> {
|
||||||
|
Ok(Self { window: None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for LV2PluginUI {
|
||||||
|
fn resumed (&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
|
||||||
|
}
|
||||||
|
fn window_event (&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
self.window.as_ref().unwrap().set_visible(false);
|
||||||
|
event_loop.exit();
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
self.window.as_ref().unwrap().request_redraw();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lv2_ui_instantiate (kind: &str) {
|
||||||
|
//let host = Suil
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ use crate::{core::*, model::*};
|
||||||
|
|
||||||
/// Application entrypoint.
|
/// Application entrypoint.
|
||||||
pub fn main () -> Usually<()> {
|
pub fn main () -> Usually<()> {
|
||||||
run(App::from_edn(include_str!("../demos/project.edn"))?
|
run(App::from_edn(include_str!("../../../demos/project.edn"))?
|
||||||
.activate(Some(|app: &Arc<RwLock<App>>|Ok({
|
.activate(Some(|app: &Arc<RwLock<App>>|Ok({
|
||||||
let (midi_in, mut midi_outs) = {
|
let (midi_in, mut midi_outs) = {
|
||||||
let app = app.read().unwrap();
|
let app = app.read().unwrap();
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
(bpm 150)
|
(bpm 150)
|
||||||
|
|
||||||
|
(midi-in "nanoKEY Studio.*capture.*")
|
||||||
(midi-in "nanoKEY Studio.*capture.*")
|
(midi-in "nanoKEY Studio.*capture.*")
|
||||||
(audio-out "Built-.+:playback_FL", "Built-.+:playback_FR")
|
(audio-out "Built-.+:playback_FL", "Built-.+:playback_FR")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,5 +19,8 @@
|
||||||
libgcc.lib
|
libgcc.lib
|
||||||
# for Panagement
|
# for Panagement
|
||||||
xorg.libX11
|
xorg.libX11
|
||||||
|
xorg.libXcursor
|
||||||
|
xorg.libXi
|
||||||
|
libxkbcommon
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue