use super::*;
impl Maolan {
pub(super) fn handle_session_message(&mut self, message: Message) -> Task<Message> {
match message {
Message::AddTrack(crate::message::AddTrack::Submit) => {
let existing_names: std::collections::HashSet<String> = self
.state
.blocking_read()
.tracks
.iter()
.map(|t| t.name.clone())
.collect();
let messages = self.add_track.create_messages();
let mut new_names = Vec::new();
for message in &messages {
let name = match message {
Message::Request(maolan_engine::message::Action::AddTrack {
name, ..
}) => Some(name.as_str()),
Message::AddTrackFromTemplate { name, .. } => Some(name.as_str()),
_ => None,
};
if let Some(name) = name {
if existing_names.contains(name) {
self.error(format!("Track '{}' already exists", name));
return Task::none();
}
new_names.push(name.to_string());
}
}
{
let mut state = self.state.blocking_write();
for name in &new_names {
state.session.ensure_track_slots(name);
}
}
let tasks: Vec<_> = messages
.into_iter()
.map(|message| self.handle_session_message(message))
.collect();
if !tasks.is_empty() {
self.modal = None;
}
Task::batch(tasks)
}
Message::AddTrackFromTemplate {
ref name,
ref template,
audio_ins,
midi_ins,
audio_outs,
midi_outs,
} => {
let task = self.send(Action::AddTrack {
name: name.clone(),
audio_ins,
midi_ins,
audio_outs,
midi_outs,
folder: false,
});
self.state
.blocking_write()
.pending_track_template_loads
.push((name.clone(), template.clone()));
task
}
Message::AddFolderFromTemplate {
ref name,
ref template,
} => {
self.modal = None;
self.apply_folder_template(name.clone(), template.clone())
}
Message::ApplyTemplate(crate::message::ApplyTemplate::Submit) => {
let (track_name, template, is_folder) = {
let state = self.state.blocking_read();
let Some(dialog) = state.apply_template_dialog.as_ref() else {
return Task::none();
};
let is_folder = state
.tracks
.iter()
.find(|t| t.name == dialog.track_name)
.is_some_and(|t| t.is_folder);
(
dialog.track_name.clone(),
dialog.selected_template.clone(),
is_folder,
)
};
let Some(template) = template else {
return Task::done(Message::Response(Err("No template selected".to_string())));
};
self.modal = None;
self.state.blocking_write().apply_template_dialog = None;
if is_folder {
self.apply_folder_template(track_name, template)
} else {
self.apply_track_template(track_name, template)
}
}
Message::ApplyTrackTemplate {
ref track_name,
ref template,
} => self.apply_track_template(track_name.clone(), template.clone()),
Message::ApplyFolderTemplate {
ref track_name,
ref template,
} => self.apply_folder_template(track_name.clone(), template.clone()),
Message::NewFromTemplate(ref template_name) => {
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
let template_path = format!(
"{}/.config/maolan/session_templates/{}",
home, template_name
);
self.state.blocking_write().message =
format!("Loading template '{}'...", template_name);
Task::perform(
async move { std::path::PathBuf::from(template_path) },
Message::LoadSessionPath,
)
}
Message::NewSession => {
if !self.state.blocking_read().hw_loaded {
return Task::none();
}
self.playing = false;
self.paused = false;
self.transport_samples = 0.0;
self.track_automation_runtime.clear();
self.touch_automation_overrides.clear();
self.touch_active_keys.clear();
self.latch_automation_overrides.clear();
self.loop_enabled = false;
self.loop_range_samples = None;
self.punch_enabled = false;
self.punch_range_samples = None;
self.last_playback_tick = None;
self.record_armed = false;
self.pending_record_after_save = false;
self.pending_save_path = None;
self.pending_save_tracks.clear();
self.pending_save_clap_tracks.clear();
self.pending_save_clap_clips.clear();
#[cfg(target_os = "macos")]
self.pending_save_vst3_states.clear();
self.pending_peak_file_loads.clear();
self.pending_peak_rebuilds.clear();
self.midi_clip_previews.clear();
self.pending_midi_clip_previews.clear();
self.session_slot_record_target = None;
self.session_dir = None;
self.session_branch = "main".to_string();
self.stop_recording_preview();
let existing_tracks: Vec<String> = {
let state = self.state.blocking_read();
state.tracks.iter().map(|t| t.name.clone()).collect()
};
let mut tasks = vec![
self.send(Action::BeginSessionRestore),
self.send(Action::Stop),
self.send(Action::SetRecordEnabled(false)),
self.send(Action::SetLoopRange(None)),
self.send(Action::SetPunchRange(None)),
self.send(Action::SetGlobalMidiLearnBinding {
target: maolan_engine::message::GlobalMidiLearnTarget::PlayPause,
binding: None,
}),
self.send(Action::SetGlobalMidiLearnBinding {
target: maolan_engine::message::GlobalMidiLearnTarget::Stop,
binding: None,
}),
self.send(Action::SetGlobalMidiLearnBinding {
target: maolan_engine::message::GlobalMidiLearnTarget::RecordToggle,
binding: None,
}),
];
for name in existing_tracks {
tasks.push(self.send(Action::RemoveTrack(name)));
}
tasks.push(self.send_modulators_to_engine());
tasks.push(self.send(Action::EndSessionRestore));
{
let mut state = self.state.blocking_write();
state.connections.clear();
state.selected.clear();
state.selected_clips.clear();
state.connection_view_selection = ConnectionViewSelection::None;
state.plugin_graph_track = None;
state.plugin_graph_clip = None;
#[cfg(all(unix, not(target_os = "macos")))]
{
state.plugin_graph_plugins.clear();
state.plugin_graph_connections.clear();
state.plugin_graphs_by_track.clear();
}
state.clap_plugins_by_track.clear();
state.clap_states_by_track.clear();
state.global_midi_learn_play_pause = None;
state.global_midi_learn_stop = None;
state.global_midi_learn_record_toggle = None;
state.session_author.clear();
state.session_album.clear();
state.session_year.clear();
state.session_track_number.clear();
state.session_genre.clear();
state.message = "New session".to_string();
state.piano = None;
state.selected_modulator_id = None;
}
self.pending_track_freeze_restore.clear();
self.pending_track_midi_editor_view_mode.clear();
self.pending_track_freeze_bounce.clear();
self.modulators.clear();
self.selected_modulator_id = None;
self.freeze_in_progress = false;
self.freeze_progress = 0.0;
self.freeze_track_name = None;
self.freeze_cancel_requested = false;
self.has_unsaved_changes = false;
self.engine_dirty = false;
self.pending_exit_after_save = false;
self.session_restore_in_progress = false;
self.last_autosave_snapshot = None;
self.pending_recovery_session_dir = None;
self.pending_autosave_recovery = None;
self.pending_open_session_dir = None;
Task::batch(tasks)
}
Message::Request(ref a) => {
if let Action::TransportPosition(sample) = a {
self.transport_samples = *sample as f64;
}
if let Some(expanded) = self.expand_request_to_folder_children(a) {
let mut tasks = Vec::with_capacity(expanded.len());
for action in expanded {
self.maybe_record_automation_from_request(&action);
tasks.push(self.send(action));
}
return Task::batch(tasks);
}
self.maybe_record_automation_from_request(a);
self.send(a.clone())
}
Message::RequestBatch(ref actions) => {
let mut tasks = Vec::with_capacity(actions.len() + 2);
tasks.push(self.send(Action::BeginHistoryGroup));
for action in actions {
if let Action::TransportPosition(sample) = action {
self.transport_samples = *sample as f64;
}
if let Some(expanded) = self.expand_request_to_folder_children(action) {
for expanded_action in expanded {
self.maybe_record_automation_from_request(&expanded_action);
tasks.push(self.send(expanded_action));
}
} else {
self.maybe_record_automation_from_request(action);
tasks.push(self.send(action.clone()));
}
}
tasks.push(self.send(Action::EndHistoryGroup));
Task::batch(tasks)
}
Message::MeterPollTick => {
if let Some(snapshot) = CLIENT.meter_snapshot() {
let action = Action::MeterSnapshot {
hw_out_db: std::sync::Arc::new(snapshot.hw_out_db),
hw_out_lufs: snapshot.hw_out_lufs,
track_meters: std::sync::Arc::new(snapshot.track_meters),
};
self.handle_response_freeze_meter_action(&action)
.unwrap_or_else(Task::none)
} else {
Task::none()
}
}
_ => Task::none(),
}
}
}