Feat: fixing stderr catching and scope not drawing completely
This commit is contained in:
@@ -158,6 +158,7 @@ struct CagireDesktop {
|
|||||||
_stream: Option<cpal::Stream>,
|
_stream: Option<cpal::Stream>,
|
||||||
_analysis_handle: Option<AnalysisHandle>,
|
_analysis_handle: Option<AnalysisHandle>,
|
||||||
midi_rx: Receiver<MidiCommand>,
|
midi_rx: Receiver<MidiCommand>,
|
||||||
|
stream_error_rx: crossbeam_channel::Receiver<String>,
|
||||||
current_font: FontChoice,
|
current_font: FontChoice,
|
||||||
zoom_factor: f32,
|
zoom_factor: f32,
|
||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
@@ -201,6 +202,7 @@ impl CagireDesktop {
|
|||||||
_stream: b.stream,
|
_stream: b.stream,
|
||||||
_analysis_handle: b.analysis_handle,
|
_analysis_handle: b.analysis_handle,
|
||||||
midi_rx: b.midi_rx,
|
midi_rx: b.midi_rx,
|
||||||
|
stream_error_rx: b.stream_error_rx,
|
||||||
current_font,
|
current_font,
|
||||||
zoom_factor,
|
zoom_factor,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
@@ -235,6 +237,9 @@ impl CagireDesktop {
|
|||||||
max_voices: self.app.audio.config.max_voices,
|
max_voices: self.app.audio.config.max_voices,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (new_error_tx, new_error_rx) = crossbeam_channel::bounded(16);
|
||||||
|
self.stream_error_rx = new_error_rx;
|
||||||
|
|
||||||
let mut restart_samples = Vec::new();
|
let mut restart_samples = Vec::new();
|
||||||
for path in &self.app.audio.config.sample_paths {
|
for path in &self.app.audio.config.sample_paths {
|
||||||
let index = doux::sampling::scan_samples_dir(path);
|
let index = doux::sampling::scan_samples_dir(path);
|
||||||
@@ -257,6 +262,7 @@ impl CagireDesktop {
|
|||||||
Arc::clone(&self.metrics),
|
Arc::clone(&self.metrics),
|
||||||
restart_samples,
|
restart_samples,
|
||||||
Arc::clone(&self.audio_sample_pos),
|
Arc::clone(&self.audio_sample_pos),
|
||||||
|
new_error_tx,
|
||||||
) {
|
) {
|
||||||
Ok((new_stream, info, new_analysis, registry)) => {
|
Ok((new_stream, info, new_analysis, registry)) => {
|
||||||
self._stream = Some(new_stream);
|
self._stream = Some(new_stream);
|
||||||
@@ -350,6 +356,11 @@ impl CagireDesktop {
|
|||||||
impl eframe::App for CagireDesktop {
|
impl eframe::App for CagireDesktop {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
self.handle_audio_restart();
|
self.handle_audio_restart();
|
||||||
|
|
||||||
|
while let Ok(err) = self.stream_error_rx.try_recv() {
|
||||||
|
self.app.ui.flash(&err, 3000, cagire::state::FlashKind::Error);
|
||||||
|
}
|
||||||
|
|
||||||
self.update_metrics();
|
self.update_metrics();
|
||||||
|
|
||||||
ctx.input(|i| {
|
ctx.input(|i| {
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ use cpal::traits::{DeviceTrait, StreamTrait};
|
|||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
use cpal::Stream;
|
use cpal::Stream;
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
use doux::{Engine, EngineMetrics};
|
use doux::{Engine, EngineMetrics};
|
||||||
|
|
||||||
@@ -274,6 +274,7 @@ pub struct AudioStreamInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn build_stream(
|
pub fn build_stream(
|
||||||
config: &AudioStreamConfig,
|
config: &AudioStreamConfig,
|
||||||
audio_rx: Receiver<AudioCommand>,
|
audio_rx: Receiver<AudioCommand>,
|
||||||
@@ -282,6 +283,7 @@ pub fn build_stream(
|
|||||||
metrics: Arc<EngineMetrics>,
|
metrics: Arc<EngineMetrics>,
|
||||||
initial_samples: Vec<doux::sampling::SampleEntry>,
|
initial_samples: Vec<doux::sampling::SampleEntry>,
|
||||||
audio_sample_pos: Arc<AtomicU64>,
|
audio_sample_pos: Arc<AtomicU64>,
|
||||||
|
error_tx: Sender<String>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
Stream,
|
Stream,
|
||||||
@@ -375,15 +377,15 @@ pub fn build_stream(
|
|||||||
|
|
||||||
engine.metrics.load.set_buffer_time(buffer_time_ns);
|
engine.metrics.load.set_buffer_time(buffer_time_ns);
|
||||||
engine.process_block(data, &[], &[]);
|
engine.process_block(data, &[], &[]);
|
||||||
scope_buffer.write(&engine.output);
|
scope_buffer.write(data);
|
||||||
|
|
||||||
// Feed mono mix to analysis thread via ring buffer (non-blocking)
|
// Feed mono mix to analysis thread via ring buffer (non-blocking)
|
||||||
for chunk in engine.output.chunks(channels) {
|
for chunk in data.chunks(channels) {
|
||||||
let mono = chunk.iter().sum::<f32>() / channels as f32;
|
let mono = chunk.iter().sum::<f32>() / channels as f32;
|
||||||
let _ = fft_producer.try_push(mono);
|
let _ = fft_producer.try_push(mono);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|err| eprintln!("stream error: {err}"),
|
move |err| { let _ = error_tx.try_send(format!("stream error: {err}")); },
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to build stream: {e}"))?;
|
.map_err(|e| format!("Failed to build stream: {e}"))?;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ pub struct Init {
|
|||||||
pub stream: Option<cpal::Stream>,
|
pub stream: Option<cpal::Stream>,
|
||||||
pub analysis_handle: Option<AnalysisHandle>,
|
pub analysis_handle: Option<AnalysisHandle>,
|
||||||
pub midi_rx: Receiver<MidiCommand>,
|
pub midi_rx: Receiver<MidiCommand>,
|
||||||
|
pub stream_error_rx: crossbeam_channel::Receiver<String>,
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
@@ -188,6 +189,8 @@ pub fn init(args: InitArgs) -> Init {
|
|||||||
seq_config,
|
seq_config,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let (stream_error_tx, stream_error_rx) = crossbeam_channel::bounded(16);
|
||||||
|
|
||||||
let stream_config = AudioStreamConfig {
|
let stream_config = AudioStreamConfig {
|
||||||
output_device: app.audio.config.output_device.clone(),
|
output_device: app.audio.config.output_device.clone(),
|
||||||
channels: app.audio.config.channels,
|
channels: app.audio.config.channels,
|
||||||
@@ -203,6 +206,7 @@ pub fn init(args: InitArgs) -> Init {
|
|||||||
Arc::clone(&metrics),
|
Arc::clone(&metrics),
|
||||||
initial_samples,
|
initial_samples,
|
||||||
Arc::clone(&audio_sample_pos),
|
Arc::clone(&audio_sample_pos),
|
||||||
|
stream_error_tx,
|
||||||
) {
|
) {
|
||||||
Ok((s, info, analysis, registry)) => {
|
Ok((s, info, analysis, registry)) => {
|
||||||
app.audio.config.sample_rate = info.sample_rate;
|
app.audio.config.sample_rate = info.sample_rate;
|
||||||
@@ -246,6 +250,7 @@ pub fn init(args: InitArgs) -> Init {
|
|||||||
stream,
|
stream,
|
||||||
analysis_handle,
|
analysis_handle,
|
||||||
midi_rx,
|
midi_rx,
|
||||||
|
stream_error_rx,
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
settings,
|
settings,
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ fn main() -> io::Result<()> {
|
|||||||
let mut _stream = b.stream;
|
let mut _stream = b.stream;
|
||||||
let mut _analysis_handle = b.analysis_handle;
|
let mut _analysis_handle = b.analysis_handle;
|
||||||
let mut midi_rx = b.midi_rx;
|
let mut midi_rx = b.midi_rx;
|
||||||
|
let mut stream_error_rx = b.stream_error_rx;
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
io::stdout().execute(EnableBracketedPaste)?;
|
io::stdout().execute(EnableBracketedPaste)?;
|
||||||
@@ -110,6 +111,9 @@ fn main() -> io::Result<()> {
|
|||||||
max_voices: app.audio.config.max_voices,
|
max_voices: app.audio.config.max_voices,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (new_error_tx, new_error_rx) = crossbeam_channel::bounded(16);
|
||||||
|
stream_error_rx = new_error_rx;
|
||||||
|
|
||||||
let mut restart_samples = Vec::new();
|
let mut restart_samples = Vec::new();
|
||||||
for path in &app.audio.config.sample_paths {
|
for path in &app.audio.config.sample_paths {
|
||||||
let index = doux::sampling::scan_samples_dir(path);
|
let index = doux::sampling::scan_samples_dir(path);
|
||||||
@@ -132,6 +136,7 @@ fn main() -> io::Result<()> {
|
|||||||
Arc::clone(&metrics),
|
Arc::clone(&metrics),
|
||||||
restart_samples,
|
restart_samples,
|
||||||
Arc::clone(&audio_sample_pos),
|
Arc::clone(&audio_sample_pos),
|
||||||
|
new_error_tx,
|
||||||
) {
|
) {
|
||||||
Ok((new_stream, info, new_analysis, registry)) => {
|
Ok((new_stream, info, new_analysis, registry)) => {
|
||||||
_stream = Some(new_stream);
|
_stream = Some(new_stream);
|
||||||
@@ -161,6 +166,10 @@ fn main() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while let Ok(err) = stream_error_rx.try_recv() {
|
||||||
|
app.ui.flash(&err, 3000, state::FlashKind::Error);
|
||||||
|
}
|
||||||
|
|
||||||
app.playback.playing = playing.load(Ordering::Relaxed);
|
app.playback.playing = playing.load(Ordering::Relaxed);
|
||||||
|
|
||||||
while let Ok(midi_cmd) = midi_rx.try_recv() {
|
while let Ok(midi_cmd) = midi_rx.try_recv() {
|
||||||
|
|||||||
Reference in New Issue
Block a user