Feat: clean the codebase as much as possible
Some checks failed
Deploy Website / deploy (push) Failing after 4m51s
Some checks failed
Deploy Website / deploy (push) Failing after 4m51s
This commit is contained in:
@@ -86,12 +86,7 @@ impl App {
|
||||
Self::build(variables, dict, rng, false)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn with_shared(variables: Variables, dict: Dictionary, rng: Rng) -> Self {
|
||||
Self::build(variables, dict, rng, false)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(dead_code)] // used by plugin crate
|
||||
pub fn new_plugin(variables: Variables, dict: Dictionary, rng: Rng) -> Self {
|
||||
Self::build(variables, dict, rng, true)
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ impl CagireDesktop {
|
||||
std::thread::Builder::new()
|
||||
.name("sample-preload".into())
|
||||
.spawn(move || {
|
||||
cagire::init::preload_sample_heads(preload_entries, sr, ®istry);
|
||||
cagire::engine::preload_sample_heads(preload_entries, sr, ®istry);
|
||||
})
|
||||
.expect("failed to spawn preload thread");
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
use cpal::traits::{DeviceTrait, StreamTrait};
|
||||
use cpal::Stream;
|
||||
use crossbeam_channel::Receiver;
|
||||
use doux::{Engine, EngineMetrics};
|
||||
use ringbuf::{traits::*, HeapRb};
|
||||
use rustfft::{num_complex::Complex, FftPlanner};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
use super::AudioCommand;
|
||||
#[cfg(feature = "cli")]
|
||||
use std::sync::atomic::AtomicU64;
|
||||
|
||||
pub struct ScopeBuffer {
|
||||
pub samples: [AtomicU32; 256],
|
||||
@@ -230,6 +227,36 @@ fn analysis_loop(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn preload_sample_heads(
|
||||
entries: Vec<(String, std::path::PathBuf)>,
|
||||
target_sr: f32,
|
||||
registry: &doux::SampleRegistry,
|
||||
) {
|
||||
let mut batch = Vec::with_capacity(entries.len());
|
||||
for (name, path) in &entries {
|
||||
match doux::sampling::decode_sample_head(path, target_sr) {
|
||||
Ok(data) => batch.push((name.clone(), Arc::new(data))),
|
||||
Err(e) => eprintln!("preload {name}: {e}"),
|
||||
}
|
||||
}
|
||||
if !batch.is_empty() {
|
||||
registry.insert_batch(batch);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
use cpal::traits::{DeviceTrait, StreamTrait};
|
||||
#[cfg(feature = "cli")]
|
||||
use cpal::Stream;
|
||||
#[cfg(feature = "cli")]
|
||||
use crossbeam_channel::Receiver;
|
||||
#[cfg(feature = "cli")]
|
||||
use doux::{Engine, EngineMetrics};
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
use super::AudioCommand;
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub struct AudioStreamConfig {
|
||||
pub output_device: Option<String>,
|
||||
pub channels: u16,
|
||||
@@ -237,12 +264,14 @@ pub struct AudioStreamConfig {
|
||||
pub max_voices: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub struct AudioStreamInfo {
|
||||
pub sample_rate: f32,
|
||||
pub host_name: String,
|
||||
pub channels: u16,
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn build_stream(
|
||||
config: &AudioStreamConfig,
|
||||
audio_rx: Receiver<AudioCommand>,
|
||||
|
||||
@@ -7,16 +7,20 @@ mod timing;
|
||||
|
||||
pub use timing::{substeps_in_window, StepTiming, SyncTime};
|
||||
|
||||
// AnalysisHandle and SequencerHandle are used by src/bin/desktop.rs
|
||||
// Used by plugin and desktop crates via the lib; not by the terminal binary directly.
|
||||
#[allow(unused_imports)]
|
||||
pub use audio::{
|
||||
build_stream, spawn_analysis_thread, AnalysisHandle, AudioStreamConfig, ScopeBuffer,
|
||||
SpectrumBuffer,
|
||||
preload_sample_heads, spawn_analysis_thread, AnalysisHandle, ScopeBuffer, SpectrumBuffer,
|
||||
};
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
#[allow(unused_imports)]
|
||||
pub use audio::{build_stream, AudioStreamConfig, AudioStreamInfo};
|
||||
|
||||
pub use link::LinkState;
|
||||
#[allow(unused_imports)]
|
||||
pub use sequencer::{
|
||||
parse_midi_command, spawn_sequencer, AudioCommand, MidiCommand, PatternChange,
|
||||
PatternSnapshot, SeqCommand, SequencerConfig, SequencerHandle, SequencerSnapshot,
|
||||
SequencerState, SharedSequencerState, StepSnapshot, TickInput, TickOutput, TimestampedCommand,
|
||||
parse_midi_command, spawn_sequencer, AudioCommand, MidiCommand, PatternChange, PatternSnapshot,
|
||||
SeqCommand, SequencerConfig, SequencerHandle, SequencerSnapshot, SequencerState,
|
||||
SharedSequencerState, StepSnapshot, TickInput, TickOutput, TimestampedCommand,
|
||||
};
|
||||
|
||||
@@ -28,27 +28,16 @@ mod memory {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_memory_locked() -> bool {
|
||||
MLOCKALL_SUCCESS.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use memory::{is_memory_locked, lock_memory};
|
||||
pub use memory::lock_memory;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn lock_memory() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[allow(dead_code)]
|
||||
pub fn is_memory_locked() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Attempts to set realtime scheduling priority for the current thread.
|
||||
/// Returns true if RT priority was successfully set, false otherwise.
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -105,7 +94,7 @@ pub fn set_realtime_priority() -> bool {
|
||||
/// - Configured rtprio limits in /etc/security/limits.conf:
|
||||
/// @audio - rtprio 95
|
||||
/// @audio - memlock unlimited
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(all(target_os = "linux", feature = "cli"))]
|
||||
pub fn set_realtime_priority() -> bool {
|
||||
use thread_priority::unix::{
|
||||
set_thread_priority_and_policy, thread_native_id, NormalThreadSchedulePolicy,
|
||||
@@ -138,17 +127,27 @@ pub fn set_realtime_priority() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", not(feature = "cli")))]
|
||||
pub fn set_realtime_priority() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix, target_os = "windows")))]
|
||||
pub fn set_realtime_priority() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(all(target_os = "windows", feature = "cli"))]
|
||||
pub fn set_realtime_priority() -> bool {
|
||||
use thread_priority::{set_current_thread_priority, ThreadPriority};
|
||||
set_current_thread_priority(ThreadPriority::Max).is_ok()
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "windows", not(feature = "cli")))]
|
||||
pub fn set_realtime_priority() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// High-precision sleep using clock_nanosleep on Linux.
|
||||
/// Uses monotonic clock for jitter-free sleeping.
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
21
src/init.rs
21
src/init.rs
@@ -7,8 +7,9 @@ use doux::EngineMetrics;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::engine::{
|
||||
build_stream, spawn_sequencer, AnalysisHandle, AudioStreamConfig, LinkState, MidiCommand,
|
||||
PatternChange, ScopeBuffer, SequencerConfig, SequencerHandle, SpectrumBuffer,
|
||||
build_stream, preload_sample_heads, spawn_sequencer, AnalysisHandle, AudioStreamConfig,
|
||||
LinkState, MidiCommand, PatternChange, ScopeBuffer, SequencerConfig, SequencerHandle,
|
||||
SpectrumBuffer,
|
||||
};
|
||||
use crate::midi;
|
||||
use crate::model;
|
||||
@@ -230,19 +231,3 @@ pub fn init(args: InitArgs) -> Init {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn preload_sample_heads(
|
||||
entries: Vec<(String, std::path::PathBuf)>,
|
||||
target_sr: f32,
|
||||
registry: &doux::SampleRegistry,
|
||||
) {
|
||||
let mut batch = Vec::with_capacity(entries.len());
|
||||
for (name, path) in &entries {
|
||||
match doux::sampling::decode_sample_head(path, target_sr) {
|
||||
Ok(data) => batch.push((name.clone(), Arc::new(data))),
|
||||
Err(e) => eprintln!("preload {name}: {e}"),
|
||||
}
|
||||
}
|
||||
if !batch.is_empty() {
|
||||
registry.insert_batch(batch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ fn load_project_samples(ctx: &mut InputContext) {
|
||||
std::thread::Builder::new()
|
||||
.name("sample-preload".into())
|
||||
.spawn(move || {
|
||||
crate::init::preload_sample_heads(all_preload_entries, sr, ®istry);
|
||||
crate::engine::preload_sample_heads(all_preload_entries, sr, ®istry);
|
||||
})
|
||||
.expect("failed to spawn preload thread");
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ pub(super) fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> Input
|
||||
std::thread::Builder::new()
|
||||
.name("sample-preload".into())
|
||||
.spawn(move || {
|
||||
crate::init::preload_sample_heads(preload_entries, sr, ®istry);
|
||||
crate::engine::preload_sample_heads(preload_entries, sr, ®istry);
|
||||
})
|
||||
.expect("failed to spawn preload thread");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub use cagire_forth as forth;
|
||||
|
||||
pub mod app;
|
||||
#[cfg(feature = "cli")]
|
||||
pub mod init;
|
||||
pub mod commands;
|
||||
pub mod engine;
|
||||
@@ -18,5 +19,5 @@ pub mod widgets;
|
||||
#[cfg(feature = "block-renderer")]
|
||||
pub mod block_renderer;
|
||||
|
||||
#[cfg(feature = "desktop")]
|
||||
#[cfg(feature = "block-renderer")]
|
||||
pub mod input_egui;
|
||||
|
||||
@@ -149,7 +149,7 @@ fn main() -> io::Result<()> {
|
||||
std::thread::Builder::new()
|
||||
.name("sample-preload".into())
|
||||
.spawn(move || {
|
||||
init::preload_sample_heads(preload_entries, sr, ®istry);
|
||||
engine::preload_sample_heads(preload_entries, sr, ®istry);
|
||||
})
|
||||
.expect("failed to spawn preload thread");
|
||||
}
|
||||
|
||||
64
src/midi.rs
64
src/midi.rs
@@ -1,8 +1,6 @@
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
use midir::{MidiInput, MidiOutput};
|
||||
|
||||
use crate::model::CcAccess;
|
||||
|
||||
pub const MAX_MIDI_OUTPUTS: usize = 4;
|
||||
@@ -22,12 +20,12 @@ impl CcMemory {
|
||||
Self(Arc::new(Mutex::new([[[0u8; 128]; 16]; MAX_MIDI_DEVICES])))
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
fn inner(&self) -> &CcMemoryInner {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Set a CC value (for testing)
|
||||
#[allow(dead_code)]
|
||||
#[allow(dead_code)] // used by integration tests
|
||||
pub fn set_cc(&self, device: usize, channel: usize, cc: usize, value: u8) {
|
||||
let mut mem = self.0.lock();
|
||||
mem[device.min(MAX_MIDI_DEVICES - 1)][channel.min(15)][cc.min(127)] = value;
|
||||
@@ -52,8 +50,9 @@ pub struct MidiDeviceInfo {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn list_midi_outputs() -> Vec<MidiDeviceInfo> {
|
||||
let Ok(midi_out) = MidiOutput::new("cagire-probe") else {
|
||||
let Ok(midi_out) = midir::MidiOutput::new("cagire-probe") else {
|
||||
return Vec::new();
|
||||
};
|
||||
midi_out
|
||||
@@ -68,8 +67,14 @@ pub fn list_midi_outputs() -> Vec<MidiDeviceInfo> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn list_midi_outputs() -> Vec<MidiDeviceInfo> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn list_midi_inputs() -> Vec<MidiDeviceInfo> {
|
||||
let Ok(midi_in) = MidiInput::new("cagire-probe") else {
|
||||
let Ok(midi_in) = midir::MidiInput::new("cagire-probe") else {
|
||||
return Vec::new();
|
||||
};
|
||||
midi_in
|
||||
@@ -84,8 +89,15 @@ pub fn list_midi_inputs() -> Vec<MidiDeviceInfo> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn list_midi_inputs() -> Vec<MidiDeviceInfo> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub struct MidiState {
|
||||
#[cfg(feature = "cli")]
|
||||
output_conns: [Option<midir::MidiOutputConnection>; MAX_MIDI_OUTPUTS],
|
||||
#[cfg(feature = "cli")]
|
||||
input_conns: [Option<midir::MidiInputConnection<(CcMemoryInner, usize)>>; MAX_MIDI_INPUTS],
|
||||
pub selected_outputs: [Option<usize>; MAX_MIDI_OUTPUTS],
|
||||
pub selected_inputs: [Option<usize>; MAX_MIDI_INPUTS],
|
||||
@@ -101,7 +113,9 @@ impl Default for MidiState {
|
||||
impl MidiState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "cli")]
|
||||
output_conns: [None, None, None, None],
|
||||
#[cfg(feature = "cli")]
|
||||
input_conns: [None, None, None, None],
|
||||
selected_outputs: [None; MAX_MIDI_OUTPUTS],
|
||||
selected_inputs: [None; MAX_MIDI_INPUTS],
|
||||
@@ -109,11 +123,13 @@ impl MidiState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn connect_output(&mut self, slot: usize, port_index: usize) -> Result<(), String> {
|
||||
if slot >= MAX_MIDI_OUTPUTS {
|
||||
return Err("Invalid output slot".to_string());
|
||||
}
|
||||
let midi_out = MidiOutput::new(&format!("cagire-out-{slot}")).map_err(|e| e.to_string())?;
|
||||
let midi_out =
|
||||
midir::MidiOutput::new(&format!("cagire-out-{slot}")).map_err(|e| e.to_string())?;
|
||||
let ports = midi_out.ports();
|
||||
let port = ports.get(port_index).ok_or("MIDI output port not found")?;
|
||||
let conn = midi_out
|
||||
@@ -124,6 +140,12 @@ impl MidiState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn connect_output(&mut self, _slot: usize, _port_index: usize) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn disconnect_output(&mut self, slot: usize) {
|
||||
if slot < MAX_MIDI_OUTPUTS {
|
||||
self.output_conns[slot] = None;
|
||||
@@ -131,11 +153,20 @@ impl MidiState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn disconnect_output(&mut self, slot: usize) {
|
||||
if slot < MAX_MIDI_OUTPUTS {
|
||||
self.selected_outputs[slot] = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn connect_input(&mut self, slot: usize, port_index: usize) -> Result<(), String> {
|
||||
if slot >= MAX_MIDI_INPUTS {
|
||||
return Err("Invalid input slot".to_string());
|
||||
}
|
||||
let midi_in = MidiInput::new(&format!("cagire-in-{slot}")).map_err(|e| e.to_string())?;
|
||||
let midi_in =
|
||||
midir::MidiInput::new(&format!("cagire-in-{slot}")).map_err(|e| e.to_string())?;
|
||||
let ports = midi_in.ports();
|
||||
let port = ports.get(port_index).ok_or("MIDI input port not found")?;
|
||||
|
||||
@@ -165,6 +196,12 @@ impl MidiState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn connect_input(&mut self, _slot: usize, _port_index: usize) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn disconnect_input(&mut self, slot: usize) {
|
||||
if slot < MAX_MIDI_INPUTS {
|
||||
self.input_conns[slot] = None;
|
||||
@@ -172,6 +209,14 @@ impl MidiState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn disconnect_input(&mut self, slot: usize) {
|
||||
if slot < MAX_MIDI_INPUTS {
|
||||
self.selected_inputs[slot] = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
fn send_message(&mut self, device: u8, message: &[u8]) {
|
||||
let slot = (device as usize).min(MAX_MIDI_OUTPUTS - 1);
|
||||
if let Some(conn) = &mut self.output_conns[slot] {
|
||||
@@ -179,6 +224,9 @@ impl MidiState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
fn send_message(&mut self, _device: u8, _message: &[u8]) {}
|
||||
|
||||
pub fn send_note_on(&mut self, device: u8, channel: u8, note: u8, velocity: u8) {
|
||||
let status = 0x90 | (channel & 0x0F);
|
||||
self.send_message(device, &[status, note & 0x7F, velocity & 0x7F]);
|
||||
|
||||
@@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::state::{ColorScheme, MainLayout};
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
const APP_NAME: &str = "cagire";
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
@@ -116,6 +117,7 @@ impl Default for LinkSettings {
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn load() -> Self {
|
||||
let mut settings: Self = confy::load(APP_NAME, None).unwrap_or_default();
|
||||
if settings.audio.channels == 0 {
|
||||
@@ -127,10 +129,19 @@ impl Settings {
|
||||
settings
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn load() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub fn save(&self) {
|
||||
if let Err(e) = confy::store(APP_NAME, None, self) {
|
||||
eprintln!("Failed to save settings: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub fn save(&self) {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user