Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderVocke committed Nov 28, 2024
1 parent 3270410 commit 6bb7772
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 98 deletions.
92 changes: 17 additions & 75 deletions src/python/shoopdaloop/lib/backend_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,37 +98,6 @@ def __init__(self, backend_state : "bindings.shoop_fx_chain_state_info_t" = None
self.active = False
self.ready = False

@dataclass
class LoopMidiChannelState:
mode: Type[PyChannelMode]
n_events_triggered : int
n_notes_active : int
length: int
start_offset: int
data_dirty : bool
played_back_sample : Any
n_preplay_samples : int

def __init__(self, backend_state : 'bindings.loop_midi_channel_state_t' = None):
if backend_state:
self.n_events_triggered = to_int(backend_state.n_events_triggered)
self.n_notes_active = to_int(backend_state.n_notes_active)
self.mode = ChannelMode(backend_state.mode)
self.length = to_int(backend_state.length)
self.start_offset = to_int(backend_state.start_offset)
self.data_dirty = bool(backend_state.data_dirty)
self.played_back_sample = (to_int(backend_state.played_back_sample) if backend_state.played_back_sample >= 0 else None)
self.n_preplay_samples = to_int(backend_state.n_preplay_samples)
else:
self.n_events_triggered = 0
self.n_notes_active = 0
self.mode = ChannelMode.Disabled
self.length = 0
self.start_offset = 0
self.data_dirty = False
self.played_back_sample = None
self.n_preplay_samples = 0

@dataclass
class AudioPortState:
input_peak: float
Expand Down Expand Up @@ -381,13 +350,8 @@ class BackendLoopMidiChannel:
def __init__(self, midi_channel: shoop_py_backend.MidiChannel):
self._midi_channel = midi_channel

def available(self):
return self._midi_channel is not None

def get_all_midi_data(self):
if self.available():
return [midi_event.to_dict() for midi_event in self._midi_channel.get_all_midi_data()]
return []
return [midi_event.to_dict() for midi_event in self._midi_channel.get_all_midi_data()]

def get_recorded_midi_msgs(self):
return [m for m in self.get_all_midi_data() if m['time'] >= 0]
Expand All @@ -396,60 +360,38 @@ def get_state_midi_msgs(self):
return [m for m in self.get_all_midi_data() if m['time'] < 0]

def load_all_midi_data(self, msgs):
if self.available():
midi_events = [shoop_py_backend.MidiEvent(time=m['time'], data=m['data']) for m in msgs]
self._midi_channel.load_all_midi_data(midi_events)
midi_events = [shoop_py_backend.MidiEvent(time=m['time'], data=m['data']) for m in msgs]
self._midi_channel.load_all_midi_data(midi_events)

def connect_input(self, port: 'BackendMidiPort'):
if self.available():
self._midi_channel.connect_input(port._obj)
def connect_input(self, port):
self._midi_channel.connect_input(port)

def connect_output(self, port: 'BackendMidiPort'):
if self.available():
self._midi_channel.connect_output(port._obj)
def connect_output(self, port):
self._midi_channel.connect_output(port)

def disconnect(self, port: 'BackendMidiPort'):
if self.available():
self._midi_channel.disconnect(port._obj)
def disconnect(self, port):
self._midi_channel.disconnect(port)

def get_state(self) -> LoopMidiChannelState:
if self.available():
state = self._midi_channel.get_state()
return LoopMidiChannelState(
mode=state.mode,
n_events_triggered=0, # Assuming default values as they are not available in MidiChannelState
n_notes_active=0,
length=0,
start_offset=state.start_offset,
data_dirty=state.data_dirty,
played_back_sample=None,
n_preplay_samples=state.n_preplay_samples
)
return LoopMidiChannelState()
def get_state(self) -> shoop_py_backend.MidiChannelState:
return self._midi_channel.get_state()

def set_mode(self, mode: Type['ChannelMode']):
if self.available():
self._midi_channel.set_mode(mode.value)
self._midi_channel.set_mode(int(mode))

def set_start_offset(self, offset):
if self.available():
self._midi_channel.set_start_offset(offset)
self._midi_channel.set_start_offset(offset)

def set_n_preplay_samples(self, n):
if self.available():
self._midi_channel.set_n_preplay_samples(n)
self._midi_channel.set_n_preplay_samples(n)

def clear_data_dirty(self):
if self.available():
self._midi_channel.clear_data_dirty()
self._midi_channel.clear_data_dirty()

def clear(self):
if self.available():
self._midi_channel.clear()
self._midi_channel.clear()

def reset_state_tracking(self):
if self.available():
self._midi_channel.reset_state_tracking()
self._midi_channel.reset_state_tracking()

class BackendAudioPort:
def __init__(self,
Expand Down
13 changes: 12 additions & 1 deletion src/rust/backend_bindings/src/midi_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ impl MidiEvent {

pub struct MidiChannelState {
pub mode: ChannelMode,
pub n_events_triggered: u32,
pub n_notes_active: u32,
pub length: u32,
pub start_offset: i32,
pub played_back_sample: Option<u32>,
pub n_preplay_samples: u32,
pub data_dirty: bool,
}
Expand All @@ -42,14 +46,21 @@ impl MidiChannelState {
pub fn new(obj: &ffi::shoop_midi_channel_state_info_t) -> Self {
MidiChannelState {
mode: ChannelMode::try_from(obj.mode).unwrap(),
n_events_triggered: obj.n_events_triggered,
n_notes_active: obj.n_notes_active,
length: obj.length,
start_offset: obj.start_offset,
played_back_sample: match obj.played_back_sample >= 0 {
true => Some(obj.played_back_sample as u32),
false => None
},
n_preplay_samples: obj.n_preplay_samples,
data_dirty: obj.data_dirty != 0,
}
}
}

struct MidiChannel {
pub struct MidiChannel {
obj : Mutex<*mut ffi::shoopdaloop_loop_midi_channel_t>,
}

Expand Down
2 changes: 1 addition & 1 deletion src/rust/shoopdaloop/src/shoop_py_backend/audio_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::shoop_py_backend::audio_port::AudioPort;
use crate::shoop_py_backend::channel::ChannelMode;

#[pyclass]
#[derive(Clone)]
pub struct AudioChannelState {
#[pyo3(get)]
pub mode : ChannelMode,
Expand Down Expand Up @@ -41,7 +42,6 @@ impl AudioChannelState {
}
}


#[pyclass]
pub struct AudioChannel {
pub obj : backend_bindings::AudioChannel,
Expand Down
10 changes: 5 additions & 5 deletions src/rust/shoopdaloop/src/shoop_py_backend/midi.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use pyo3::prelude::*;

pub fn register_in_module<'py>(m: &PyModule) -> PyResult<()> {
m.add_class::<MidiEvent>()?;
Ok(())
}

#[pyclass]
#[derive(Clone)]
pub struct MidiEvent {
Expand All @@ -30,3 +25,8 @@ impl From<backend_bindings::MidiEvent> for MidiEvent {
}
}
}

pub fn register_in_module<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
m.add_class::<MidiEvent>()?;
Ok(())
}
37 changes: 21 additions & 16 deletions src/rust/shoopdaloop/src/shoop_py_backend/midi_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,42 @@

use pyo3::prelude::*;
use pyo3::exceptions::PyRuntimeError;
use backend_bindings::{MidiChannel as BackendMidiChannel, MidiChannelState as BackendMidiChannelState};

use crate::shoop_py_backend::midi_port::MidiPort;

use crate::shoop_py_backend::midi::MidiEvent;
use crate::shoop_py_backend::channel::ChannelMode;

impl From<backend_bindings::MidiEvent> for MidiEvent {
fn from(event: backend_bindings::MidiEvent) -> Self {
MidiEvent {
time: event.time,
data: event.data,
}
}
}

#[pyclass]
#[derive(Clone)]
pub struct MidiChannelState {
#[pyo3(get)]
pub mode: ChannelMode,
#[pyo3(get)]
pub n_events_triggered: u32,
#[pyo3(get)]
pub n_notes_active: u32,
#[pyo3(get)]
pub length: u32,
#[pyo3(get)]
pub start_offset: i32,
#[pyo3(get)]
pub played_back_sample: Option<u32>,
#[pyo3(get)]
pub n_preplay_samples: u32,
#[pyo3(get)]
pub data_dirty: bool,
}

impl From<BackendMidiChannelState> for MidiChannelState {
fn from(state: BackendMidiChannelState) -> Self {
impl MidiChannelState {
fn new(state: backend_bindings::MidiChannelState) -> Self {
MidiChannelState {
mode: state.mode,
mode: ChannelMode::try_from(state.mode).unwrap(),
n_events_triggered: state.n_events_triggered,
n_notes_active: state.n_notes_active,
length: state.length,
start_offset: state.start_offset,
played_back_sample: state.played_back_sample,
n_preplay_samples: state.n_preplay_samples,
data_dirty: state.data_dirty,
}
Expand All @@ -57,7 +62,7 @@ impl MidiChannel {
}

fn load_all_midi_data(&self, py: Python, msgs: Vec<MidiEvent>) -> PyResult<()> {
let backend_msgs: Vec<BackendMidiEvent> = msgs.into_iter().map(|msg| BackendMidiEvent {
let backend_msgs: Vec<backend_bindings::MidiEvent> = msgs.into_iter().map(|msg| backend_bindings::MidiEvent {
time: msg.time,
data: msg.data,
}).collect();
Expand All @@ -84,11 +89,11 @@ impl MidiChannel {
let state = self.obj.get_state().map_err(|e| {
PyErr::new::<PyRuntimeError, _>(format!("Get state failed: {:?}", e))
})?;
Ok(MidiChannelState::from(state))
Ok(MidiChannelState::new(state))
}

fn set_mode(&self, mode: i32) -> PyResult<()> {
let mode = ChannelMode::try_from(mode).map_err(|_| {
let mode = backend_bindings::ChannelMode::try_from(mode).map_err(|_| {
PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid channel mode")
})?;
self.obj.set_mode(mode);
Expand Down

0 comments on commit 6bb7772

Please sign in to comment.