Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderVocke committed Nov 25, 2024
1 parent 9576096 commit 6fdca25
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/backend/libshoopdaloop_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1907,7 +1907,7 @@ void destroy_midi_channel(shoopdaloop_loop_midi_channel_t *d) {

void destroy_shoopdaloop_decoupled_midi_port(shoopdaloop_decoupled_midi_port_t *d) {
return api_impl<void, log_level_debug_trace, log_level_warning>("destroy_shoopdaloop_decoupled_midi_port", [&]() {
logging::log<"Backend.API", log_level_error>(std::nullopt, std::nullopt, "destroy_shoopdaloop_decoupled_midi_port");
logging::log<"Backend.API", log_level_debug>(std::nullopt, std::nullopt, "destroy_shoopdaloop_decoupled_midi_port");
throw std::runtime_error("unimplemented");
});
}
Expand Down
48 changes: 21 additions & 27 deletions src/python/shoopdaloop/lib/backend_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,17 +766,19 @@ def set_ringbuffer_n_samples(self, n):
bindings.set_audio_port_ringbuffer_n_samples(self.get_backend_obj(), n)

class BackendDecoupledMidiPort:
def __init__(self, c_handle : 'POINTER(bindings.shoopdaloop_decoupled_midi_port_t)',
backend : 'BackendSession'):
self._c_handle = c_handle
self._backend = backend
def __init__(self, obj):
self._obj = obj

def available(self):
return self._c_handle and self._backend and self._backend.active()
return self.get_backend_obj()

def get_backend_obj(self):
addr = self._obj.unsafe_backend_ptr()
return cast(c_void_p(addr), POINTER(bindings.shoopdaloop_decoupled_midi_port_t))

def maybe_next_message(self):
if self.available():
r = bindings.maybe_next_message(self._c_handle)
r = bindings.maybe_next_message(self.get_backend_obj())
if r:
rval = MidiEvent(r[0])
bindings.destroy_midi_event(r)
Expand All @@ -785,14 +787,9 @@ def maybe_next_message(self):

def name(self):
if self.available():
return bindings.get_decoupled_midi_port_name(self._c_handle).decode('ascii')
return bindings.get_decoupled_midi_port_name(self.get_backend_obj()).decode('ascii')
return '(unknown)'

def destroy(self):
if self.available():
bindings.close_decoupled_midi_port(self._c_handle)
self._c_handle = 0

def send_midi(self, msg):
if self.available():
data_type = c_ubyte * len(msg)
Expand All @@ -818,11 +815,6 @@ def disconnect_external_port(self, name):
if self.available():
bindings.disconnect_external_decoupled_midi_port(self._c_handle, name.encode('ascii'))

def __del__(self):
if self.available():
self.destroy()
self._c_handle = None

class BackendMidiPort:
def __init__(self,
obj):
Expand Down Expand Up @@ -997,9 +989,7 @@ def create_loop(self) -> Type['BackendLoop']:

def create_fx_chain(self, chain_type : Type['FXChainType'], title: str) -> Type['BackendFXChain']:
if self.active():
handle = bindings.create_fx_chain(self.get_backend_obj(), chain_type.value, title.encode('ascii'))
rval = BackendFXChain(handle, chain_type, self)
return rval
return BackendFXChain(self)
return None

def get_fx_chain_audio_input_port(self, fx_chain : Type['BackendFXChain'], idx : int):
Expand Down Expand Up @@ -1081,13 +1071,6 @@ def get_state(self):
bindings.destroy_audio_driver_state(state)
return rval

def open_decoupled_midi_port(self, name_hint : str, direction : int) -> 'BackendDecoupledMidiPort':
if self.active():
handle = bindings.open_decoupled_midi_port(self.get_backend_obj(), name_hint.encode('ascii'), direction)
port = BackendDecoupledMidiPort(handle, self)
return port
raise Exception("Trying to open a MIDI port before audio driver is started.")

def dummy_enter_controlled_mode(self):
if self.active():
bindings.dummy_audio_enter_controlled_mode(self.get_backend_obj())
Expand Down Expand Up @@ -1170,6 +1153,17 @@ def open_driver_audio_port(backend_session, audio_driver, name_hint : str, direc
return BackendAudioPort(obj)
raise Exception("Failed to open audio port: backend session or audio driver not active")

def open_driver_decoupled_midi_port(audio_driver, name_hint : str, direction : int) -> 'BackendDecoupledMidiPort':
if audio_driver.active():
obj = shoop_py_backend.open_driver_decoupled_midi_port(
audio_driver._obj,
name_hint,
direction
)
port = BackendDecoupledMidiPort(obj)
return port
raise Exception("Trying to open a MIDI port before audio driver is started.")

def open_driver_midi_port(backend_session, audio_driver, name_hint : str, direction : int, min_n_ringbuffer_samples : int) -> 'BackendMidiPort':
if backend_session.active() and audio_driver.active():
obj = shoop_py_backend.open_driver_midi_port(
Expand Down
8 changes: 5 additions & 3 deletions src/python/shoopdaloop/lib/q_objects/MidiControlPort.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,6 @@ def set_property(auto_property_name, value):
@ShoopSlot()
def close(self):
self.logger.trace(lambda: "Closing.")
if self._backend_obj:
self._backend_obj.destroy()
self._backend_obj = None
self._autoconnect_regexes = []
self.autoconnect_update()
Expand All @@ -344,7 +342,11 @@ def maybe_init(self):
return
if self._name_hint and self._backend and self._direction != None and self._may_open:
self.logger.debug(lambda: "Opening decoupled MIDI port {}".format(self._name_hint))
self._backend_obj = self._backend.get_backend_driver_obj().open_decoupled_midi_port(self._name_hint, self._direction)
self._backend_obj = open_driver_decoupled_midi_port(
self._backend.get_backend_driver_obj(),
self._name_hint,
self._direction
)

if not self._backend_obj:
self.logger.error(lambda: "Failed to open decoupled MIDI port {}".format(self._name_hint))
Expand Down
2 changes: 1 addition & 1 deletion src/rust/backend_bindings/src/audio_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl AudioPort {
audio_driver : &AudioDriver,
name_hint : &str,
direction : &PortDirection,
min_n_ringbuffer_samples : u32) -> Result<Self, anyhow::Error>
min_n_ringbuffer_samples : u32) -> Result<Self, anyhow::Error>
{
let name_hint_ptr = name_hint.as_ptr() as *const i8;
let obj = unsafe { ffi::open_driver_audio_port
Expand Down
55 changes: 55 additions & 0 deletions src/rust/backend_bindings/src/decoupled_midi_port.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use anyhow;
use crate::ffi;
use std::sync::Mutex;

use crate::audio_driver::AudioDriver;
use crate::port::PortDirection;

pub struct DecoupledMidiPort {
obj : Mutex<*mut ffi::shoopdaloop_decoupled_midi_port_t>,
}

unsafe impl Send for DecoupledMidiPort {}
unsafe impl Sync for DecoupledMidiPort {}

impl DecoupledMidiPort {
pub fn new_driver_port(audio_driver : &AudioDriver,
name_hint : &str,
direction : &PortDirection) -> Result<Self, anyhow::Error>
{
let name_hint_ptr = name_hint.as_ptr() as *const i8;
let obj = unsafe { ffi::open_decoupled_midi_port
(audio_driver.unsafe_backend_ptr(),
name_hint_ptr,
*direction as u32) };
if obj.is_null() {
return Err(anyhow::anyhow!("Failed to create audio port"));
}
Ok(DecoupledMidiPort {
obj : Mutex::new(obj),
})
}

pub fn unsafe_port_from_raw_ptr(ptr : usize) -> Self {
DecoupledMidiPort {
obj : Mutex::new(ptr as *mut ffi::shoopdaloop_decoupled_midi_port_t),
}
}

pub unsafe fn unsafe_backend_ptr(&self) -> *mut ffi::shoopdaloop_decoupled_midi_port_t {
let guard = self.obj.lock().unwrap();
*guard
}
}

impl Drop for DecoupledMidiPort {
fn drop(&mut self) {
let guard = self.obj.lock().unwrap();
let obj = *guard;
if obj.is_null() {
return;
}
unsafe { ffi::close_decoupled_midi_port(obj) };
unsafe { ffi::destroy_shoopdaloop_decoupled_midi_port(obj) };
}
}
56 changes: 56 additions & 0 deletions src/rust/backend_bindings/src/fx_chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use anyhow;
use crate::ffi;
use std::sync::Mutex;
use crate::integer_enum;

use crate::backend_session::BackendSession;

integer_enum! {
pub enum FXChainType {
CarlaRack = ffi::shoop_fx_chain_type_t_Carla_Rack,
CarlaPatchbay = ffi::shoop_fx_chain_type_t_Carla_Patchbay,
CarlaPatchbay16x = ffi::shoop_fx_chain_type_t_Carla_Patchbay_16x,
Test2x2x1 = ffi::shoop_fx_chain_type_t_Test2x2x1,
}
}

pub struct FXChain {
obj : Mutex<*mut ffi::shoopdaloop_fx_chain_t>,
}

unsafe impl Send for FXChain {}
unsafe impl Sync for FXChain {}

impl FXChain {
pub fn new(backend_session : &BackendSession,
chain_type : &FXChainType,
title : &str) -> Result<Self, anyhow::Error> {
let title_ptr = title.as_ptr() as *const i8;
let obj = unsafe { ffi::create_fx_chain
(backend_session.unsafe_backend_ptr(),
*chain_type as u32,
title_ptr) };
if obj.is_null() {
return Err(anyhow::anyhow!("Failed to create FX chain"));
}
Ok(FXChain {
obj : Mutex::new(obj),
})
}

pub unsafe fn unsafe_backend_ptr(&self) -> *mut ffi::shoopdaloop_fx_chain_t {
let guard = self.obj.lock().unwrap();
*guard
}
}

impl Drop for FXChain {
fn drop(&mut self) {
let guard = self.obj.lock().unwrap();
let obj = *guard;
if obj.is_null() {
return;
}
unsafe { ffi::destroy_fx_chain(obj) };
}
}
11 changes: 10 additions & 1 deletion src/rust/backend_bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,21 @@ mod common;
#[cfg(not(feature = "prebuild"))]
pub use common::*;

#[cfg(not(feature = "prebuild"))]
mod decoupled_midi_port;
#[cfg(not(feature = "prebuild"))]
pub use decoupled_midi_port::*;

#[cfg(not(feature = "prebuild"))]
mod fx_chain;
#[cfg(not(feature = "prebuild"))]
pub use fx_chain::*;

#[cfg(not(feature = "prebuild"))]
mod midi_channel;
#[cfg(not(feature = "prebuild"))]
pub use midi_channel::*;


#[cfg(not(feature = "prebuild"))]
mod midi_port;
#[cfg(not(feature = "prebuild"))]
Expand Down
38 changes: 38 additions & 0 deletions src/rust/shoopdaloop/src/shoop_py_backend/decoupled_midi_port.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// See audio_port.rs from backend_bindings

use pyo3::prelude::*;
// use pyo3::exceptions::*;
use backend_bindings;

use crate::shoop_py_backend::audio_driver::AudioDriver;

#[pyclass]
pub struct DecoupledMidiPort {
pub obj : backend_bindings::DecoupledMidiPort,
}

#[pymethods]
impl DecoupledMidiPort {
fn unsafe_backend_ptr (&self) -> usize {
unsafe { self.obj.unsafe_backend_ptr() as usize }
}
}

#[pyfunction]
pub fn open_driver_decoupled_midi_port<'py>
(audio_driver : &AudioDriver,
name_hint : &str,
direction : i32) -> PyResult<DecoupledMidiPort> {
let dir = backend_bindings::PortDirection::try_from(direction).unwrap();
Ok(DecoupledMidiPort { obj: backend_bindings::DecoupledMidiPort::new_driver_port
(&audio_driver.obj,
name_hint,
&dir)
.unwrap() })
}

pub fn register_in_module<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
m.add_class::<DecoupledMidiPort>()?;
m.add_function(wrap_pyfunction!(open_driver_decoupled_midi_port, m)?)?;
Ok(())
}
34 changes: 34 additions & 0 deletions src/rust/shoopdaloop/src/shoop_py_backend/fx_chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// See shoop_loop.rs from backend_bindings

use pyo3::prelude::*;
// use pyo3::exceptions::*;
use backend_bindings;

use crate::shoop_py_backend::backend_session::BackendSession;

#[pyclass]
pub struct FXChain {
pub obj : backend_bindings::FXChain,
}

#[pymethods]
impl FXChain {
#[new]
fn py_new(backend_session : &BackendSession,
chain_type : u32,
title : &str) -> PyResult<Self> {
Ok(FXChain { obj: backend_bindings::FXChain::new
(&backend_session.obj,
&backend_bindings::FXChainType::try_from(chain_type).unwrap(),
title).unwrap() })
}

fn unsafe_backend_ptr (&self) -> usize {
unsafe { self.obj.unsafe_backend_ptr() as usize }
}
}

pub fn register_in_module<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
m.add_class::<FXChain>()?;
Ok(())
}
4 changes: 4 additions & 0 deletions src/rust/shoopdaloop/src/shoop_py_backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ mod audio_channel;
mod audio_driver;
mod audio_port;
mod backend_session;
mod decoupled_midi_port;
mod fx_chain;
mod midi_channel;
mod midi_port;
mod resample;
Expand All @@ -18,6 +20,8 @@ pub fn create_py_module<'py>(
audio_driver::register_in_module(&m)?;
audio_port::register_in_module(&m)?;
backend_session::register_in_module(&m)?;
decoupled_midi_port::register_in_module(&m)?;
fx_chain::register_in_module(&m)?;
midi_channel::register_in_module(&m)?;
midi_port::register_in_module(&m)?;
shoop_loop::register_in_module(&m)?;
Expand Down

0 comments on commit 6fdca25

Please sign in to comment.