Skip to content

Commit ea671e4

Browse files
committed
Refactor manager and a few others
1 parent be1c455 commit ea671e4

File tree

5 files changed

+33
-95
lines changed

5 files changed

+33
-95
lines changed

README.md

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,12 @@
1-
# Rustortion 🎸
1+
# Rustortion
22

33
A basic low-latency guitar amp simulator, built in Rust using JACK (via PipeWire).
44

5+
This is heavily under development, probably broken, so use at your own risk.
6+
57
## Requirements
68

79
- Linux with PipeWire (with JACK support enabled)
810
- `libjack` and JACK tools installed
911
- Rust: [https://rust-lang.org/tools/install](https://rust-lang.org/tools/install)
1012

11-
## Building
12-
13-
`cargo build --release`
14-
15-
## Running
16-
17-
Pass the path to the preset file as an argument:
18-
`cargo run -- --preset-path presets/metal.json`
19-
20-
Use the recording flag to save the output to a file:
21-
`cargo run -- --recording`
22-
23-
Recordings are saved in the /recordings directory with a time-stamp in the filename.
24-
25-
## TODO
26-
27-
- Add real distortion effects (soft/hard clipping)
28-
- Real-time gain control
29-
- Cabinet IR loading

presets/clean.json

Lines changed: 0 additions & 10 deletions
This file was deleted.

presets/metal.json

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/io/manager.rs

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use crossbeam::channel::{Sender, bounded};
22
use jack::{AsyncClient, Client, ClientOptions, contrib::ClosureProcessHandler};
3+
use log::error;
34

45
use crate::io::processor::{ProcessHandler, Processor};
56
use crate::io::recorder::Recorder;
67
use crate::sim::chain::AmplifierChain;
78

89
/// Manages the audio processing chain and JACK client
910
pub struct ProcessorManager {
10-
client: Option<Client>,
11-
active_client: Option<AsyncClient<Notifications, ClosureProcessHandler<(), ProcessHandler>>>,
11+
_active_client: AsyncClient<Notifications, ClosureProcessHandler<(), ProcessHandler>>,
1212
recorder: Option<Recorder>,
1313
/// GUI → audio thread: push a completely new preset
14-
amp_tx: Option<Sender<Box<AmplifierChain>>>,
14+
amp_tx: Sender<Box<AmplifierChain>>,
1515
sample_rate: f32,
1616
}
1717

@@ -25,80 +25,58 @@ impl ProcessorManager {
2525
let (client, _status) = Client::new("rustortion", ClientOptions::NO_START_SERVER)
2626
.map_err(|e| format!("Failed to create JACK client: {e}"))?;
2727

28+
let (tx_amp, rx_amp) = bounded::<Box<AmplifierChain>>(1); // SPSC, size 1
29+
30+
let processor = Processor::new_with_channel(&client, rx_amp, None);
31+
let handler = ClosureProcessHandler::new(processor.into_process_handler());
32+
33+
let sample_rate = client.sample_rate() as f32;
34+
35+
let _active_client = client
36+
.activate_async(Notifications, handler)
37+
.map_err(|e| format!("activate_async: {e}"))?;
38+
2839
Ok(Self {
29-
sample_rate: client.sample_rate() as f32,
30-
client: Some(client),
31-
active_client: None,
40+
sample_rate,
41+
_active_client,
3242
recorder: None,
33-
amp_tx: None,
43+
amp_tx: tx_amp,
3444
})
3545
}
3646

3747
/// Push a new amplifier chain from the GUI side.
3848
/// Never blocks; silently drops if the buffer is full.
3949
pub fn set_amp_chain(&self, new_chain: AmplifierChain) {
40-
if let Some(tx) = &self.amp_tx {
41-
tx.try_send(Box::new(new_chain)).unwrap_or_else(|e| {
42-
log::error!("Failed to send new amplifier chain: {e}");
50+
self.amp_tx
51+
.try_send(Box::new(new_chain))
52+
.unwrap_or_else(|e| {
53+
error!("Failed to send new amplifier chain: {e}");
4354
});
44-
}
4555
}
4656

4757
/// Starts recording if enabled
4858
pub fn enable_recording(&mut self, record_dir: &str) -> Result<(), String> {
4959
if self.recorder.is_some() {
50-
return Ok(()); // Already recording
60+
return Ok(());
5161
}
5262

5363
let recorder = Recorder::new(self.sample_rate as u32, record_dir)
5464
.map_err(|e| format!("Failed to start recorder: {e}"))?;
65+
let _ = recorder.sender();
5566

5667
self.recorder = Some(recorder);
5768
Ok(())
5869
}
5970

6071
/// Stops recording if active
6172
pub fn disable_recording(&mut self) {
62-
if let Some(recorder) = self.recorder.take() {
63-
recorder.stop();
64-
}
65-
}
66-
67-
/// Starts the audio processing
68-
pub fn start(&mut self) -> Result<(), String> {
69-
if self.active_client.is_some() {
70-
return Ok(());
73+
if self.recorder.is_none() {
74+
return;
7175
}
7276

73-
let client = self.client.take().ok_or("Client already active")?;
74-
let (tx_amp, rx_amp) = bounded::<Box<AmplifierChain>>(1); // SPSC, size 1
75-
76-
let tx_audio = self.recorder.as_ref().map(|r| r.sender());
77-
78-
// Processor owns its mutable chain and a receiver for updates
79-
let processor = Processor::new_with_channel(&client, rx_amp, tx_audio);
80-
81-
let callback = processor.into_process_handler();
82-
let handler = ClosureProcessHandler::new(callback);
83-
let active = client
84-
.activate_async(Notifications, handler)
85-
.map_err(|e| format!("activate_async: {e}"))?;
86-
87-
self.amp_tx = Some(tx_amp);
88-
self.active_client = Some(active);
89-
Ok(())
90-
}
91-
92-
pub fn stop(&mut self) -> Result<(), String> {
93-
if let Some(active) = self.active_client.take() {
94-
let (client, _n, _h) = active
95-
.deactivate()
96-
.map_err(|e| format!("deactivate: {e:?}"))?;
97-
self.client = Some(client);
77+
if let Some(recorder) = self.recorder.take() {
78+
recorder.stop();
9879
}
99-
self.disable_recording();
100-
self.amp_tx = None;
101-
Ok(())
10280
}
10381

10482
/// Returns the sample rate
@@ -109,6 +87,6 @@ impl ProcessorManager {
10987

11088
impl Drop for ProcessorManager {
11189
fn drop(&mut self) {
112-
let _ = self.stop();
90+
self.disable_recording();
11391
}
11492
}

src/main.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ fn main() -> Result<(), String> {
3333
let recording = args.recording;
3434

3535
info!(
36-
"🔥 Rustortion: {}",
36+
"Rustortion: {}",
3737
if recording { "🛑 Recording!" } else { "" }
3838
);
3939

@@ -45,15 +45,13 @@ fn main() -> Result<(), String> {
4545
processor_manager.enable_recording("./recordings")?;
4646
}
4747

48-
processor_manager.start()?;
49-
5048
processor_manager.set_amp_chain(chain);
5149

5250
let running = Arc::new(AtomicBool::new(true));
5351
let r = Arc::clone(&running);
5452

5553
ctrlc::set_handler(move || {
56-
info!("\nCtrl+C received, shutting down...");
54+
info!("Ctrl+C received, shutting down...");
5755
r.store(false, Ordering::SeqCst);
5856
})
5957
.expect("Error setting Ctrl+C handler");
@@ -62,7 +60,5 @@ fn main() -> Result<(), String> {
6260
thread::sleep(Duration::from_secs(1));
6361
}
6462

65-
processor_manager.stop()?;
66-
6763
Ok(())
6864
}

0 commit comments

Comments
 (0)