Feat: clean the codebase as much as possible
This commit is contained in:
@@ -25,7 +25,7 @@ use cagire::model::{Dictionary, Rng, Variables};
|
||||
use cagire::theme;
|
||||
use cagire::views;
|
||||
|
||||
use crate::input_egui::{convert_egui_events, convert_egui_mouse};
|
||||
use cagire::input_egui::{convert_egui_events, convert_egui_mouse};
|
||||
use crate::params::CagireParams;
|
||||
use crate::PluginBridge;
|
||||
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
|
||||
use nih_plug_egui::egui;
|
||||
use ratatui::layout::Rect;
|
||||
|
||||
pub fn convert_egui_mouse(
|
||||
ctx: &egui::Context,
|
||||
widget_rect: egui::Rect,
|
||||
term: Rect,
|
||||
) -> Vec<MouseEvent> {
|
||||
let mut events = Vec::new();
|
||||
if widget_rect.width() < 1.0
|
||||
|| widget_rect.height() < 1.0
|
||||
|| term.width == 0
|
||||
|| term.height == 0
|
||||
{
|
||||
return events;
|
||||
}
|
||||
|
||||
ctx.input(|i| {
|
||||
let Some(pos) = i.pointer.latest_pos() else {
|
||||
return;
|
||||
};
|
||||
if !widget_rect.contains(pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
let col =
|
||||
((pos.x - widget_rect.left()) / widget_rect.width() * term.width as f32) as u16;
|
||||
let row =
|
||||
((pos.y - widget_rect.top()) / widget_rect.height() * term.height as f32) as u16;
|
||||
let col = col.min(term.width.saturating_sub(1));
|
||||
let row = row.min(term.height.saturating_sub(1));
|
||||
|
||||
if i.pointer.button_clicked(egui::PointerButton::Primary) {
|
||||
events.push(MouseEvent {
|
||||
kind: MouseEventKind::Down(MouseButton::Left),
|
||||
column: col,
|
||||
row,
|
||||
modifiers: KeyModifiers::empty(),
|
||||
});
|
||||
}
|
||||
|
||||
let scroll = i.raw_scroll_delta.y;
|
||||
if scroll > 1.0 {
|
||||
events.push(MouseEvent {
|
||||
kind: MouseEventKind::ScrollUp,
|
||||
column: col,
|
||||
row,
|
||||
modifiers: KeyModifiers::empty(),
|
||||
});
|
||||
} else if scroll < -1.0 {
|
||||
events.push(MouseEvent {
|
||||
kind: MouseEventKind::ScrollDown,
|
||||
column: col,
|
||||
row,
|
||||
modifiers: KeyModifiers::empty(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
events
|
||||
}
|
||||
|
||||
pub fn convert_egui_events(ctx: &egui::Context) -> Vec<KeyEvent> {
|
||||
let mut events = Vec::new();
|
||||
|
||||
for event in &ctx.input(|i| i.events.clone()) {
|
||||
if let Some(key_event) = convert_event(event) {
|
||||
events.push(key_event);
|
||||
}
|
||||
}
|
||||
|
||||
events
|
||||
}
|
||||
|
||||
fn convert_event(event: &egui::Event) -> Option<KeyEvent> {
|
||||
match event {
|
||||
egui::Event::Key {
|
||||
key,
|
||||
pressed,
|
||||
modifiers,
|
||||
..
|
||||
} => {
|
||||
if !*pressed {
|
||||
return None;
|
||||
}
|
||||
let mods = convert_modifiers(*modifiers);
|
||||
if is_character_key(*key)
|
||||
&& !mods.intersects(KeyModifiers::CONTROL | KeyModifiers::ALT)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let code = convert_key(*key)?;
|
||||
Some(KeyEvent::new(code, mods))
|
||||
}
|
||||
egui::Event::Text(text) => {
|
||||
if text.len() == 1 {
|
||||
let c = text.chars().next()?;
|
||||
if !c.is_control() {
|
||||
return Some(KeyEvent::new(KeyCode::Char(c), KeyModifiers::empty()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
egui::Event::Copy => Some(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL)),
|
||||
egui::Event::Cut => Some(KeyEvent::new(KeyCode::Char('x'), KeyModifiers::CONTROL)),
|
||||
egui::Event::Paste(_) => Some(KeyEvent::new(KeyCode::Char('v'), KeyModifiers::CONTROL)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_key(key: egui::Key) -> Option<KeyCode> {
|
||||
Some(match key {
|
||||
egui::Key::ArrowDown => KeyCode::Down,
|
||||
egui::Key::ArrowLeft => KeyCode::Left,
|
||||
egui::Key::ArrowRight => KeyCode::Right,
|
||||
egui::Key::ArrowUp => KeyCode::Up,
|
||||
egui::Key::Escape => KeyCode::Esc,
|
||||
egui::Key::Tab => KeyCode::Tab,
|
||||
egui::Key::Backspace => KeyCode::Backspace,
|
||||
egui::Key::Enter => KeyCode::Enter,
|
||||
egui::Key::Space => KeyCode::Char(' '),
|
||||
egui::Key::Insert => KeyCode::Insert,
|
||||
egui::Key::Delete => KeyCode::Delete,
|
||||
egui::Key::Home => KeyCode::Home,
|
||||
egui::Key::End => KeyCode::End,
|
||||
egui::Key::PageUp => KeyCode::PageUp,
|
||||
egui::Key::PageDown => KeyCode::PageDown,
|
||||
egui::Key::F1 => KeyCode::F(1),
|
||||
egui::Key::F2 => KeyCode::F(2),
|
||||
egui::Key::F3 => KeyCode::F(3),
|
||||
egui::Key::F4 => KeyCode::F(4),
|
||||
egui::Key::F5 => KeyCode::F(5),
|
||||
egui::Key::F6 => KeyCode::F(6),
|
||||
egui::Key::F7 => KeyCode::F(7),
|
||||
egui::Key::F8 => KeyCode::F(8),
|
||||
egui::Key::F9 => KeyCode::F(9),
|
||||
egui::Key::F10 => KeyCode::F(10),
|
||||
egui::Key::F11 => KeyCode::F(11),
|
||||
egui::Key::F12 => KeyCode::F(12),
|
||||
egui::Key::A => KeyCode::Char('a'),
|
||||
egui::Key::B => KeyCode::Char('b'),
|
||||
egui::Key::C => KeyCode::Char('c'),
|
||||
egui::Key::D => KeyCode::Char('d'),
|
||||
egui::Key::E => KeyCode::Char('e'),
|
||||
egui::Key::F => KeyCode::Char('f'),
|
||||
egui::Key::G => KeyCode::Char('g'),
|
||||
egui::Key::H => KeyCode::Char('h'),
|
||||
egui::Key::I => KeyCode::Char('i'),
|
||||
egui::Key::J => KeyCode::Char('j'),
|
||||
egui::Key::K => KeyCode::Char('k'),
|
||||
egui::Key::L => KeyCode::Char('l'),
|
||||
egui::Key::M => KeyCode::Char('m'),
|
||||
egui::Key::N => KeyCode::Char('n'),
|
||||
egui::Key::O => KeyCode::Char('o'),
|
||||
egui::Key::P => KeyCode::Char('p'),
|
||||
egui::Key::Q => KeyCode::Char('q'),
|
||||
egui::Key::R => KeyCode::Char('r'),
|
||||
egui::Key::S => KeyCode::Char('s'),
|
||||
egui::Key::T => KeyCode::Char('t'),
|
||||
egui::Key::U => KeyCode::Char('u'),
|
||||
egui::Key::V => KeyCode::Char('v'),
|
||||
egui::Key::W => KeyCode::Char('w'),
|
||||
egui::Key::X => KeyCode::Char('x'),
|
||||
egui::Key::Y => KeyCode::Char('y'),
|
||||
egui::Key::Z => KeyCode::Char('z'),
|
||||
egui::Key::Num0 => KeyCode::Char('0'),
|
||||
egui::Key::Num1 => KeyCode::Char('1'),
|
||||
egui::Key::Num2 => KeyCode::Char('2'),
|
||||
egui::Key::Num3 => KeyCode::Char('3'),
|
||||
egui::Key::Num4 => KeyCode::Char('4'),
|
||||
egui::Key::Num5 => KeyCode::Char('5'),
|
||||
egui::Key::Num6 => KeyCode::Char('6'),
|
||||
egui::Key::Num7 => KeyCode::Char('7'),
|
||||
egui::Key::Num8 => KeyCode::Char('8'),
|
||||
egui::Key::Num9 => KeyCode::Char('9'),
|
||||
egui::Key::Minus => KeyCode::Char('-'),
|
||||
egui::Key::Equals => KeyCode::Char('='),
|
||||
egui::Key::OpenBracket => KeyCode::Char('['),
|
||||
egui::Key::CloseBracket => KeyCode::Char(']'),
|
||||
egui::Key::Semicolon => KeyCode::Char(';'),
|
||||
egui::Key::Comma => KeyCode::Char(','),
|
||||
egui::Key::Period => KeyCode::Char('.'),
|
||||
egui::Key::Slash => KeyCode::Char('/'),
|
||||
egui::Key::Backslash => KeyCode::Char('\\'),
|
||||
egui::Key::Backtick => KeyCode::Char('`'),
|
||||
egui::Key::Quote => KeyCode::Char('\''),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn convert_modifiers(mods: egui::Modifiers) -> KeyModifiers {
|
||||
let mut result = KeyModifiers::empty();
|
||||
if mods.shift {
|
||||
result |= KeyModifiers::SHIFT;
|
||||
}
|
||||
if mods.ctrl || mods.command {
|
||||
result |= KeyModifiers::CONTROL;
|
||||
}
|
||||
if mods.alt {
|
||||
result |= KeyModifiers::ALT;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn is_character_key(key: egui::Key) -> bool {
|
||||
matches!(
|
||||
key,
|
||||
egui::Key::A
|
||||
| egui::Key::B
|
||||
| egui::Key::C
|
||||
| egui::Key::D
|
||||
| egui::Key::E
|
||||
| egui::Key::F
|
||||
| egui::Key::G
|
||||
| egui::Key::H
|
||||
| egui::Key::I
|
||||
| egui::Key::J
|
||||
| egui::Key::K
|
||||
| egui::Key::L
|
||||
| egui::Key::M
|
||||
| egui::Key::N
|
||||
| egui::Key::O
|
||||
| egui::Key::P
|
||||
| egui::Key::Q
|
||||
| egui::Key::R
|
||||
| egui::Key::S
|
||||
| egui::Key::T
|
||||
| egui::Key::U
|
||||
| egui::Key::V
|
||||
| egui::Key::W
|
||||
| egui::Key::X
|
||||
| egui::Key::Y
|
||||
| egui::Key::Z
|
||||
| egui::Key::Num0
|
||||
| egui::Key::Num1
|
||||
| egui::Key::Num2
|
||||
| egui::Key::Num3
|
||||
| egui::Key::Num4
|
||||
| egui::Key::Num5
|
||||
| egui::Key::Num6
|
||||
| egui::Key::Num7
|
||||
| egui::Key::Num8
|
||||
| egui::Key::Num9
|
||||
| egui::Key::Space
|
||||
| egui::Key::Minus
|
||||
| egui::Key::Equals
|
||||
| egui::Key::OpenBracket
|
||||
| egui::Key::CloseBracket
|
||||
| egui::Key::Semicolon
|
||||
| egui::Key::Comma
|
||||
| egui::Key::Period
|
||||
| egui::Key::Slash
|
||||
| egui::Key::Backslash
|
||||
| egui::Key::Backtick
|
||||
| egui::Key::Quote
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
mod editor;
|
||||
mod input_egui;
|
||||
mod params;
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -51,6 +50,8 @@ pub struct CagirePlugin {
|
||||
rng: Rng,
|
||||
cmd_buffer: String,
|
||||
audio_buffer: Vec<f32>,
|
||||
output_channels: usize,
|
||||
scope_extract_buffer: Vec<f32>,
|
||||
fft_producer: Option<ringbuf::HeapProd<f32>>,
|
||||
_analysis: Option<AnalysisHandle>,
|
||||
pending_note_offs: Vec<PendingNoteOff>,
|
||||
@@ -88,6 +89,8 @@ impl Default for CagirePlugin {
|
||||
rng,
|
||||
cmd_buffer: String::with_capacity(256),
|
||||
audio_buffer: Vec::new(),
|
||||
output_channels: 2,
|
||||
scope_extract_buffer: Vec::new(),
|
||||
fft_producer: None,
|
||||
_analysis: None,
|
||||
pending_note_offs: Vec::new(),
|
||||
@@ -105,19 +108,36 @@ impl Plugin for CagirePlugin {
|
||||
const EMAIL: &'static str = "raphael.forment@gmail.com";
|
||||
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[AudioIOLayout {
|
||||
main_input_channels: None,
|
||||
main_output_channels: Some(new_nonzero_u32(2)),
|
||||
aux_input_ports: &[],
|
||||
aux_output_ports: &[],
|
||||
names: PortNames {
|
||||
layout: Some("Stereo"),
|
||||
main_input: None,
|
||||
main_output: Some("Output"),
|
||||
aux_inputs: &[],
|
||||
aux_outputs: &[],
|
||||
const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[
|
||||
AudioIOLayout {
|
||||
main_input_channels: None,
|
||||
main_output_channels: Some(new_nonzero_u32(2)),
|
||||
aux_input_ports: &[],
|
||||
aux_output_ports: &[new_nonzero_u32(2); 7],
|
||||
names: PortNames {
|
||||
layout: Some("Multi-output"),
|
||||
main_input: None,
|
||||
main_output: Some("Orbit 0"),
|
||||
aux_inputs: &[],
|
||||
aux_outputs: &[
|
||||
"Orbit 1", "Orbit 2", "Orbit 3", "Orbit 4", "Orbit 5", "Orbit 6", "Orbit 7",
|
||||
],
|
||||
},
|
||||
},
|
||||
}];
|
||||
AudioIOLayout {
|
||||
main_input_channels: None,
|
||||
main_output_channels: Some(new_nonzero_u32(2)),
|
||||
aux_input_ports: &[],
|
||||
aux_output_ports: &[],
|
||||
names: PortNames {
|
||||
layout: Some("Stereo"),
|
||||
main_input: None,
|
||||
main_output: Some("Output"),
|
||||
aux_inputs: &[],
|
||||
aux_outputs: &[],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const MIDI_INPUT: MidiConfig = MidiConfig::MidiCCs;
|
||||
const MIDI_OUTPUT: MidiConfig = MidiConfig::MidiCCs;
|
||||
@@ -139,7 +159,7 @@ impl Plugin for CagirePlugin {
|
||||
|
||||
fn initialize(
|
||||
&mut self,
|
||||
_audio_io_layout: &AudioIOLayout,
|
||||
audio_io_layout: &AudioIOLayout,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl InitContext<Self>,
|
||||
) -> bool {
|
||||
@@ -147,6 +167,9 @@ impl Plugin for CagirePlugin {
|
||||
self.sample_pos = 0;
|
||||
self.prev_beat = -1.0;
|
||||
|
||||
let num_aux = audio_io_layout.aux_output_ports.len();
|
||||
self.output_channels = 2 + num_aux * 2;
|
||||
|
||||
self.seq_state = Some(SequencerState::new(
|
||||
Arc::clone(&self.variables),
|
||||
Arc::clone(&self.dict),
|
||||
@@ -156,7 +179,7 @@ impl Plugin for CagirePlugin {
|
||||
|
||||
let engine = doux::Engine::new_with_channels(
|
||||
self.sample_rate,
|
||||
2,
|
||||
self.output_channels,
|
||||
64,
|
||||
);
|
||||
self.bridge
|
||||
@@ -217,7 +240,7 @@ impl Plugin for CagirePlugin {
|
||||
fn process(
|
||||
&mut self,
|
||||
buffer: &mut Buffer,
|
||||
_aux: &mut AuxiliaryBuffers,
|
||||
aux: &mut AuxiliaryBuffers,
|
||||
context: &mut impl ProcessContext<Self>,
|
||||
) -> ProcessStatus {
|
||||
let Some(seq_state) = &mut self.seq_state else {
|
||||
@@ -399,34 +422,45 @@ impl Plugin for CagirePlugin {
|
||||
engine.evaluate(cmd_ref);
|
||||
}
|
||||
|
||||
// Process audio block — doux writes interleaved stereo into our buffer
|
||||
let num_samples = buffer_len * 2;
|
||||
self.audio_buffer.resize(num_samples, 0.0);
|
||||
// Process audio block — doux writes interleaved into our buffer
|
||||
let total_samples = buffer_len * self.output_channels;
|
||||
self.audio_buffer.resize(total_samples, 0.0);
|
||||
self.audio_buffer.fill(0.0);
|
||||
engine.process_block(&mut self.audio_buffer, &[], &[]);
|
||||
|
||||
// Feed scope and spectrum analysis
|
||||
self.bridge.scope_buffer.write(&self.audio_buffer);
|
||||
// Feed scope and spectrum analysis (orbit 0 only)
|
||||
if self.output_channels == 2 {
|
||||
self.bridge.scope_buffer.write(&self.audio_buffer);
|
||||
} else {
|
||||
self.scope_extract_buffer.resize(buffer_len * 2, 0.0);
|
||||
for i in 0..buffer_len {
|
||||
self.scope_extract_buffer[i * 2] =
|
||||
self.audio_buffer[i * self.output_channels];
|
||||
self.scope_extract_buffer[i * 2 + 1] =
|
||||
self.audio_buffer[i * self.output_channels + 1];
|
||||
}
|
||||
self.bridge.scope_buffer.write(&self.scope_extract_buffer);
|
||||
}
|
||||
if let Some(producer) = &mut self.fft_producer {
|
||||
for chunk in self.audio_buffer.chunks(2) {
|
||||
let mono = (chunk[0] + chunk.get(1).copied().unwrap_or(0.0)) * 0.5;
|
||||
let _ = producer.try_push(mono);
|
||||
let stride = self.output_channels;
|
||||
for i in 0..buffer_len {
|
||||
let left = self.audio_buffer[i * stride];
|
||||
let right = self.audio_buffer[i * stride + 1];
|
||||
let _ = producer.try_push((left + right) * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy interleaved doux output → nih-plug channel slices
|
||||
let mut channel_iter = buffer.iter_samples();
|
||||
for frame_idx in 0..buffer_len {
|
||||
if let Some(mut frame) = channel_iter.next() {
|
||||
let left = self.audio_buffer[frame_idx * 2];
|
||||
let right = self.audio_buffer[frame_idx * 2 + 1];
|
||||
if let Some(sample) = frame.get_mut(0) {
|
||||
*sample = left;
|
||||
}
|
||||
if let Some(sample) = frame.get_mut(1) {
|
||||
*sample = right;
|
||||
}
|
||||
}
|
||||
// De-interleave doux output into nih-plug channel buffers
|
||||
let stride = self.output_channels;
|
||||
deinterleave_stereo(&self.audio_buffer, buffer.as_slice(), stride, 0, buffer_len);
|
||||
for (aux_idx, aux_buf) in aux.outputs.iter_mut().enumerate() {
|
||||
deinterleave_stereo(
|
||||
&self.audio_buffer,
|
||||
aux_buf.as_slice(),
|
||||
stride,
|
||||
(aux_idx + 1) * 2,
|
||||
buffer_len,
|
||||
);
|
||||
}
|
||||
|
||||
self.sample_pos += buffer_len as u64;
|
||||
@@ -445,6 +479,7 @@ impl ClapPlugin for CagirePlugin {
|
||||
ClapFeature::Instrument,
|
||||
ClapFeature::Synthesizer,
|
||||
ClapFeature::Stereo,
|
||||
ClapFeature::Surround,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -454,8 +489,23 @@ impl Vst3Plugin for CagirePlugin {
|
||||
Vst3SubCategory::Instrument,
|
||||
Vst3SubCategory::Synth,
|
||||
Vst3SubCategory::Stereo,
|
||||
Vst3SubCategory::Surround,
|
||||
];
|
||||
}
|
||||
|
||||
fn deinterleave_stereo(
|
||||
interleaved: &[f32],
|
||||
out: &mut [&mut [f32]],
|
||||
stride: usize,
|
||||
ch_offset: usize,
|
||||
len: usize,
|
||||
) {
|
||||
let (left, right) = out.split_at_mut(1);
|
||||
for (i, (l, r)) in left[0][..len].iter_mut().zip(right[0][..len].iter_mut()).enumerate() {
|
||||
*l = interleaved[i * stride + ch_offset];
|
||||
*r = interleaved[i * stride + ch_offset + 1];
|
||||
}
|
||||
}
|
||||
|
||||
nih_export_clap!(CagirePlugin);
|
||||
nih_export_vst3!(CagirePlugin);
|
||||
|
||||
Reference in New Issue
Block a user