audio-oxide/au-o2-gui/src/editor/engine_tick.rs

216 lines
11 KiB
Rust

use std::time::Instant;
use iced::Task;
use super::{decode_region_audio, Editor, Message, StatusLevel};
use crate::engine::{EngineCommand, EngineEvent};
use crate::region::{Region, TakeFolder};
use crate::waveform::WaveformPeaks;
impl Editor {
pub(crate) fn handle_engine_tick(&mut self) -> Task<Message> {
if let Some((ref msg, level, t)) = self.status_message {
if t.elapsed().as_secs() >= 5 {
self.last_status = Some((msg.clone(), level));
self.status_message = None;
}
}
if let Some(ref engine) = self.engine {
for event in engine.poll_events() {
match event {
EngineEvent::TransportPosition(pos) => {
self.current_position = pos;
let sample_pos = pos.to_samples_mapped(
&self.tempo_map, self.project_config.sample_rate,
self.time_signature_numerator as u32,
);
for track in &mut self.tracks {
if track.automation_mode.reads() {
for lane in &track.automation_lanes {
if let Some(val) = lane.value_at(sample_pos) {
match &lane.target {
crate::automation::AutomationTarget::Volume => track.volume = val,
crate::automation::AutomationTarget::Pan => track.pan = val,
crate::automation::AutomationTarget::Mute => track.muted = val > 0.5,
crate::automation::AutomationTarget::ModuleParam { module_id, key } => {
self.module_params.values.insert((*module_id, key.clone()), val);
}
}
}
}
}
}
}
EngineEvent::Error(e) => {
debug_log!("engine error: {}", e);
self.status_message = Some((e, StatusLevel::Error, Instant::now()));
}
EngineEvent::BusCreated => {}
EngineEvent::GraphRebuilt => {}
EngineEvent::ModuleLoaded { bus_name, module_id, module_type, plugin_name, has_gui, gui_descriptor } => {
if self.routing.handle_module_loaded(&bus_name, module_id, module_type, plugin_name, &mut self.tracks) {
self.dirty = true;
}
self.module_gui.handle_module_loaded(module_id, has_gui, gui_descriptor);
}
EngineEvent::ContractViolation { module_id: _module_id, module_name: _module_name, avg_ns: _avg_ns, budget_ns: _budget_ns } => {
debug_log!("contract violation: module {}({}) {}ns / {}ns budget",
_module_name, _module_id, _avg_ns, _budget_ns);
}
EngineEvent::BufferAutoIncreased { new_size, latency_ms: _latency_ms, reason: _reason } => {
debug_log!("buffer auto-increased to {} ({:.1}ms): {}",
new_size, _latency_ms, _reason);
self.project_config.output_buffer_size = new_size as u32;
self.dirty = true;
}
EngineEvent::BufferNegotiation { module_id: _module_id, required_samples: _required_samples, required_ms: _required_ms, current_samples: _current_samples, current_ms: _current_ms } => {
debug_log!("buffer negotiation: module {} needs {} samples ({:.1}ms), current {} ({:.1}ms)",
_module_id, _required_samples, _required_ms, _current_samples, _current_ms);
}
EngineEvent::ModuleDisabled { module_id: _module_id, reason: _reason } => {
debug_log!("module {} disabled: {}", _module_id, _reason);
}
EngineEvent::AudioConfigResolved {
output_device: _output_device, input_device: _input_device, sample_rate: _sample_rate,
} => {
debug_log!("[audio] output='{}' input='{}' rate={}Hz",
_output_device,
if _input_device.is_empty() { "none" } else { &_input_device },
_sample_rate);
}
EngineEvent::RecordingComplete {
bus_name, file_path, start_sample,
length_samples, start_time, duration,
} => {
let region = Region::with_audio(
start_time,
duration,
file_path.clone(),
start_sample,
length_samples,
);
let region_id = region.id;
let abs_path = self.project_path.join(&file_path);
if let Some((audio_l, audio_r)) = decode_region_audio(&abs_path, self.project_config.sample_rate) {
self.waveform_cache.insert(
region_id,
WaveformPeaks::from_stereo(&audio_l, &audio_r),
);
engine.send(EngineCommand::LoadRegionAudio {
bus_name: bus_name.clone(),
region_id,
start_sample,
audio_l,
audio_r,
fade_in_samples: 0,
fade_out_samples: 0,
});
}
for track in &mut self.tracks {
if track.bus_name == bus_name {
track.regions.push(region);
self.dirty = true;
break;
}
}
}
EngineEvent::ModuleParamDescriptors { module_id, descriptors } => {
for desc in &descriptors {
let default_val = match &desc.kind {
oxforge::mdk::ParamKind::Float { default, .. } => *default,
oxforge::mdk::ParamKind::Bool { default } => if *default { 1.0 } else { 0.0 },
oxforge::mdk::ParamKind::Choice { default, .. } => *default as f32,
oxforge::mdk::ParamKind::Int { default, .. } => *default as f32,
};
self.module_params.values
.entry((module_id, desc.key.clone()))
.or_insert(default_val);
}
self.module_params.descriptors.insert(module_id, descriptors);
}
EngineEvent::ModuleParamChanged { module_id, key, value } => {
self.module_params.values.insert((module_id, key), value);
}
EngineEvent::PluginsDiscovered { plugins } => {
debug_log!("[plugins] discovered {} plugins", plugins.len());
self.discovered_plugins = plugins;
}
EngineEvent::TakeRecordingComplete { bus_name, takes } => {
let mut region_ids = Vec::new();
for (i, take) in takes.iter().enumerate() {
let region = Region::with_audio(
take.start_time,
take.duration,
take.file_path.clone(),
take.start_sample,
take.length_samples,
);
let region_id = region.id;
region_ids.push(region_id);
let abs_path = self.project_path.join(&take.file_path);
if let Some((audio_l, audio_r)) = decode_region_audio(&abs_path, self.project_config.sample_rate) {
self.waveform_cache.insert(
region_id,
WaveformPeaks::from_stereo(&audio_l, &audio_r),
);
if i == takes.len() - 1 {
engine.send(EngineCommand::LoadRegionAudio {
bus_name: bus_name.clone(),
region_id,
start_sample: take.start_sample,
audio_l,
audio_r,
fade_in_samples: 0,
fade_out_samples: 0,
});
}
}
for track in &mut self.tracks {
if track.bus_name == bus_name {
track.regions.push(region);
break;
}
}
}
if region_ids.len() > 1 {
let folder = TakeFolder::new(region_ids);
for track in &mut self.tracks {
if track.bus_name == bus_name {
track.take_folders.push(folder);
self.dirty = true;
break;
}
}
}
}
EngineEvent::MeterUpdate { bus_peaks, master_peak } => {
for (name, l, r) in bus_peaks {
self.meter_levels.insert(name, (l, r));
}
self.master_meter = master_peak;
}
EngineEvent::ModuleGuiDescriptorReady { module_id, descriptor } => {
self.module_gui.handle_gui_descriptor_ready(module_id, descriptor);
}
EngineEvent::ModuleGuiReady => {}
EngineEvent::ModuleErrorReport { .. } => {}
}
}
}
let gui_tasks = self.module_gui.tick(
self.engine.as_ref(),
&mut self.module_params,
&self.routing.module_names,
);
if !gui_tasks.is_empty() {
return Task::batch(gui_tasks);
}
Task::none()
}
}