318 lines
13 KiB
Rust
318 lines
13 KiB
Rust
use crossterm::event::{KeyCode, KeyEvent};
|
|
use std::sync::atomic::Ordering;
|
|
|
|
use super::{InputContext, InputResult};
|
|
use crate::commands::AppCommand;
|
|
use crate::engine::{AudioCommand, SeqCommand};
|
|
use crate::state::{ConfirmAction, DeviceKind, EngineSection, LinkSetting, Modal, SettingKind};
|
|
|
|
pub(crate) fn cycle_engine_setting(ctx: &mut InputContext, right: bool) {
|
|
let sign = if right { 1 } else { -1 };
|
|
match ctx.app.audio.setting_kind {
|
|
SettingKind::Channels => ctx.dispatch(AppCommand::AdjustAudioSetting {
|
|
setting: SettingKind::Channels,
|
|
delta: sign,
|
|
}),
|
|
SettingKind::BufferSize => ctx.dispatch(AppCommand::AdjustAudioSetting {
|
|
setting: SettingKind::BufferSize,
|
|
delta: sign * 64,
|
|
}),
|
|
SettingKind::Polyphony => ctx.dispatch(AppCommand::AdjustAudioSetting {
|
|
setting: SettingKind::Polyphony,
|
|
delta: sign,
|
|
}),
|
|
SettingKind::Nudge => {
|
|
let prev = ctx.nudge_us.load(Ordering::Relaxed);
|
|
let new_val = prev + sign as i64 * 1000;
|
|
ctx.nudge_us
|
|
.store(new_val.clamp(-100_000, 100_000), Ordering::Relaxed);
|
|
}
|
|
}
|
|
ctx.app.save_settings(ctx.link);
|
|
}
|
|
|
|
pub(crate) fn cycle_link_setting(ctx: &mut InputContext, right: bool) {
|
|
match ctx.app.audio.link_setting {
|
|
LinkSetting::Enabled => ctx.link.set_enabled(!ctx.link.is_enabled()),
|
|
LinkSetting::StartStopSync => ctx
|
|
.link
|
|
.set_start_stop_sync_enabled(!ctx.link.is_start_stop_sync_enabled()),
|
|
LinkSetting::Quantum => {
|
|
let delta = if right { 1.0 } else { -1.0 };
|
|
ctx.link.set_quantum(ctx.link.quantum() + delta);
|
|
}
|
|
}
|
|
ctx.app.save_settings(ctx.link);
|
|
}
|
|
|
|
pub(crate) fn cycle_midi_output(ctx: &mut InputContext, right: bool) {
|
|
let slot = ctx.app.audio.midi_output_slot;
|
|
let all_devices = crate::midi::list_midi_outputs();
|
|
let selected = ctx.app.midi.selected_outputs();
|
|
let available: Vec<(usize, &crate::midi::MidiDeviceInfo)> = all_devices
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|(idx, _)| {
|
|
selected[slot] == Some(*idx)
|
|
|| !selected
|
|
.iter()
|
|
.enumerate()
|
|
.any(|(s, sel)| s != slot && *sel == Some(*idx))
|
|
})
|
|
.collect();
|
|
let total_options = available.len() + 1;
|
|
let current_pos = selected[slot]
|
|
.and_then(|idx| available.iter().position(|(i, _)| *i == idx))
|
|
.map(|p| p + 1)
|
|
.unwrap_or(0);
|
|
let new_pos = if right {
|
|
(current_pos + 1) % total_options
|
|
} else if current_pos == 0 {
|
|
total_options - 1
|
|
} else {
|
|
current_pos - 1
|
|
};
|
|
if new_pos == 0 {
|
|
ctx.app.midi.disconnect_output(slot);
|
|
ctx.dispatch(AppCommand::SetStatus(format!(
|
|
"MIDI output {slot}: disconnected"
|
|
)));
|
|
} else {
|
|
let (device_idx, device) = available[new_pos - 1];
|
|
if ctx.app.midi.connect_output(slot, device_idx).is_ok() {
|
|
ctx.dispatch(AppCommand::SetStatus(format!(
|
|
"MIDI output {}: {}",
|
|
slot, device.name
|
|
)));
|
|
}
|
|
}
|
|
ctx.app.save_settings(ctx.link);
|
|
}
|
|
|
|
pub(crate) fn cycle_midi_input(ctx: &mut InputContext, right: bool) {
|
|
let slot = ctx.app.audio.midi_input_slot;
|
|
let all_devices = crate::midi::list_midi_inputs();
|
|
let available: Vec<(usize, &crate::midi::MidiDeviceInfo)> = all_devices
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|(idx, _)| {
|
|
ctx.app.midi.selected_inputs[slot] == Some(*idx)
|
|
|| !ctx
|
|
.app
|
|
.midi
|
|
.selected_inputs
|
|
.iter()
|
|
.enumerate()
|
|
.any(|(s, sel)| s != slot && *sel == Some(*idx))
|
|
})
|
|
.collect();
|
|
let total_options = available.len() + 1;
|
|
let current_pos = ctx.app.midi.selected_inputs[slot]
|
|
.and_then(|idx| available.iter().position(|(i, _)| *i == idx))
|
|
.map(|p| p + 1)
|
|
.unwrap_or(0);
|
|
let new_pos = if right {
|
|
(current_pos + 1) % total_options
|
|
} else if current_pos == 0 {
|
|
total_options - 1
|
|
} else {
|
|
current_pos - 1
|
|
};
|
|
if new_pos == 0 {
|
|
ctx.app.midi.disconnect_input(slot);
|
|
ctx.dispatch(AppCommand::SetStatus(format!(
|
|
"MIDI input {slot}: disconnected"
|
|
)));
|
|
} else {
|
|
let (device_idx, device) = available[new_pos - 1];
|
|
if ctx.app.midi.connect_input(slot, device_idx).is_ok() {
|
|
ctx.dispatch(AppCommand::SetStatus(format!(
|
|
"MIDI input {}: {}",
|
|
slot, device.name
|
|
)));
|
|
}
|
|
}
|
|
ctx.app.save_settings(ctx.link);
|
|
}
|
|
|
|
pub(super) fn handle_engine_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
|
|
match key.code {
|
|
KeyCode::Char('q') if !ctx.app.plugin_mode => {
|
|
ctx.dispatch(AppCommand::OpenModal(Modal::Confirm {
|
|
action: ConfirmAction::Quit,
|
|
selected: false,
|
|
}));
|
|
}
|
|
KeyCode::Tab => ctx.dispatch(AppCommand::AudioNextSection),
|
|
KeyCode::BackTab => ctx.dispatch(AppCommand::AudioPrevSection),
|
|
KeyCode::Up => match ctx.app.audio.section {
|
|
EngineSection::Devices if !ctx.app.plugin_mode => match ctx.app.audio.device_kind {
|
|
DeviceKind::Output => ctx.dispatch(AppCommand::AudioOutputListUp),
|
|
DeviceKind::Input => ctx.dispatch(AppCommand::AudioInputListUp),
|
|
},
|
|
EngineSection::Settings => {
|
|
ctx.dispatch(AppCommand::AudioSettingPrev);
|
|
}
|
|
EngineSection::Link => {
|
|
ctx.app.audio.prev_link_setting();
|
|
}
|
|
EngineSection::MidiOutput => {
|
|
ctx.app.audio.prev_midi_output_slot();
|
|
}
|
|
EngineSection::MidiInput => {
|
|
ctx.app.audio.prev_midi_input_slot();
|
|
}
|
|
EngineSection::Samples => {
|
|
ctx.app.audio.sample_list.move_up();
|
|
}
|
|
_ => {}
|
|
},
|
|
KeyCode::Down => match ctx.app.audio.section {
|
|
EngineSection::Devices if !ctx.app.plugin_mode => match ctx.app.audio.device_kind {
|
|
DeviceKind::Output => {
|
|
let count = ctx.app.audio.output_devices.len();
|
|
ctx.dispatch(AppCommand::AudioOutputListDown(count));
|
|
}
|
|
DeviceKind::Input => {
|
|
let count = ctx.app.audio.input_devices.len();
|
|
ctx.dispatch(AppCommand::AudioInputListDown(count));
|
|
}
|
|
},
|
|
EngineSection::Settings => {
|
|
ctx.dispatch(AppCommand::AudioSettingNext);
|
|
}
|
|
EngineSection::Link => {
|
|
ctx.app.audio.next_link_setting();
|
|
}
|
|
EngineSection::MidiOutput => {
|
|
ctx.app.audio.next_midi_output_slot();
|
|
}
|
|
EngineSection::MidiInput => {
|
|
ctx.app.audio.next_midi_input_slot();
|
|
}
|
|
EngineSection::Samples => {
|
|
let count = ctx.app.audio.config.sample_paths.len();
|
|
ctx.app.audio.sample_list.move_down(count);
|
|
}
|
|
_ => {}
|
|
},
|
|
KeyCode::PageUp => {
|
|
if !ctx.app.plugin_mode && ctx.app.audio.section == EngineSection::Devices {
|
|
match ctx.app.audio.device_kind {
|
|
DeviceKind::Output => ctx.dispatch(AppCommand::AudioOutputPageUp),
|
|
DeviceKind::Input => ctx.app.audio.input_list.page_up(),
|
|
}
|
|
}
|
|
}
|
|
KeyCode::PageDown => {
|
|
if !ctx.app.plugin_mode && ctx.app.audio.section == EngineSection::Devices {
|
|
match ctx.app.audio.device_kind {
|
|
DeviceKind::Output => {
|
|
let count = ctx.app.audio.output_devices.len();
|
|
ctx.dispatch(AppCommand::AudioOutputPageDown(count));
|
|
}
|
|
DeviceKind::Input => {
|
|
let count = ctx.app.audio.input_devices.len();
|
|
ctx.dispatch(AppCommand::AudioInputPageDown(count));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
KeyCode::Enter => {
|
|
if !ctx.app.plugin_mode && ctx.app.audio.section == EngineSection::Devices {
|
|
match ctx.app.audio.device_kind {
|
|
DeviceKind::Output => {
|
|
let cursor = ctx.app.audio.output_list.cursor;
|
|
if cursor < ctx.app.audio.output_devices.len() {
|
|
let index = ctx.app.audio.output_devices[cursor].index;
|
|
ctx.dispatch(AppCommand::SetOutputDevice(index.to_string()));
|
|
ctx.app.save_settings(ctx.link);
|
|
}
|
|
}
|
|
DeviceKind::Input => {
|
|
let cursor = ctx.app.audio.input_list.cursor;
|
|
if cursor < ctx.app.audio.input_devices.len() {
|
|
let index = ctx.app.audio.input_devices[cursor].index;
|
|
ctx.dispatch(AppCommand::SetInputDevice(index.to_string()));
|
|
ctx.app.save_settings(ctx.link);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
KeyCode::Left => match ctx.app.audio.section {
|
|
EngineSection::Devices if !ctx.app.plugin_mode => {
|
|
ctx.dispatch(AppCommand::SetDeviceKind(DeviceKind::Output));
|
|
}
|
|
EngineSection::Settings => cycle_engine_setting(ctx, false),
|
|
EngineSection::Link => cycle_link_setting(ctx, false),
|
|
EngineSection::MidiOutput => cycle_midi_output(ctx, false),
|
|
EngineSection::MidiInput => cycle_midi_input(ctx, false),
|
|
_ => {}
|
|
},
|
|
KeyCode::Right => match ctx.app.audio.section {
|
|
EngineSection::Devices if !ctx.app.plugin_mode => {
|
|
ctx.dispatch(AppCommand::SetDeviceKind(DeviceKind::Input));
|
|
}
|
|
EngineSection::Settings => cycle_engine_setting(ctx, true),
|
|
EngineSection::Link => cycle_link_setting(ctx, true),
|
|
EngineSection::MidiOutput => cycle_midi_output(ctx, true),
|
|
EngineSection::MidiInput => cycle_midi_input(ctx, true),
|
|
_ => {}
|
|
},
|
|
KeyCode::Char('R') if !ctx.app.plugin_mode => {
|
|
ctx.dispatch(AppCommand::AudioTriggerRestart);
|
|
}
|
|
KeyCode::Char('A') if ctx.app.audio.section == EngineSection::Samples => {
|
|
use crate::state::file_browser::FileBrowserState;
|
|
let mut state = FileBrowserState::new_load(String::new());
|
|
state.compute_audio_counts();
|
|
ctx.dispatch(AppCommand::OpenModal(Modal::AddSamplePath(Box::new(state))));
|
|
}
|
|
KeyCode::Char('D') => {
|
|
if ctx.app.audio.section == EngineSection::Samples {
|
|
let cursor = ctx.app.audio.sample_list.cursor;
|
|
ctx.dispatch(AppCommand::RemoveSamplePath(cursor));
|
|
ctx.app.save_settings(ctx.link);
|
|
} else if !ctx.app.plugin_mode {
|
|
ctx.dispatch(AppCommand::AudioRefreshDevices);
|
|
let out_count = ctx.app.audio.output_devices.len();
|
|
let in_count = ctx.app.audio.input_devices.len();
|
|
ctx.dispatch(AppCommand::SetStatus(format!(
|
|
"Found {out_count} output, {in_count} input devices"
|
|
)));
|
|
}
|
|
}
|
|
KeyCode::Char('h') => {
|
|
if !ctx.app.plugin_mode {
|
|
let _ = ctx.audio_tx.load().send(AudioCommand::Hush);
|
|
}
|
|
}
|
|
KeyCode::Char('p') => {
|
|
if !ctx.app.plugin_mode {
|
|
let _ = ctx.audio_tx.load().send(AudioCommand::Panic);
|
|
}
|
|
let _ = ctx.seq_cmd_tx.send(SeqCommand::StopAll);
|
|
}
|
|
KeyCode::Char('r') => ctx.dispatch(AppCommand::ResetPeakVoices),
|
|
KeyCode::Char('t') if !ctx.app.plugin_mode => {
|
|
let _ = ctx.audio_tx.load().send(AudioCommand::Evaluate {
|
|
cmd: "/sound/sine/gate/0.5/decay/0.2".into(),
|
|
tick: None,
|
|
});
|
|
}
|
|
KeyCode::Char('s') => super::open_save(ctx),
|
|
KeyCode::Char('l') => super::open_load(ctx),
|
|
KeyCode::Char('?') => {
|
|
ctx.dispatch(AppCommand::OpenModal(Modal::KeybindingsHelp { scroll: 0 }));
|
|
}
|
|
KeyCode::Char(' ') if !ctx.app.plugin_mode => {
|
|
ctx.dispatch(AppCommand::TogglePlaying);
|
|
ctx.playing
|
|
.store(ctx.app.playback.playing, Ordering::Relaxed);
|
|
}
|
|
_ => {}
|
|
}
|
|
InputResult::Continue
|
|
}
|