1use crate::api::{
2 Command, FrameTime, LooperCommand, LooperMode, LooperSpeed, Part, PartSet, QuantizationMode,
3};
4use crate::music::MetricStructure;
5use arrayvec::ArrayVec;
6use crossbeam_channel::{bounded, Receiver, Sender, TrySendError};
7use std::borrow::Cow;
8use std::io;
9use std::io::{ErrorKind, Write};
10
11pub const WAVEFORM_DOWNSAMPLE: usize = 2048;
12
13#[derive(Copy, Clone, Eq, PartialEq, Debug)]
14pub enum EngineState {
15 Stopped,
16 Paused,
17 Active,
18}
19
20#[derive(Copy, Clone, Debug)]
21pub struct EngineStateSnapshot {
22 pub engine_state: EngineState,
23 pub time: FrameTime,
24 pub metric_structure: MetricStructure,
25 pub active_looper: u32,
26 pub looper_count: usize,
27 pub part: Part,
28 pub solo: bool,
29 pub sync_mode: QuantizationMode,
30 pub input_levels: [u8; 2],
31 pub looper_levels: [[u8; 2]; 64],
32 pub metronome_volume: f32,
33}
34
35pub type Waveform = [Vec<f32>; 2];
36
37#[derive(Copy, Clone, PartialEq, Debug)]
38pub struct LooperState {
39 pub mode: LooperMode,
40 pub speed: LooperSpeed,
41 pub pan: f32,
42 pub level: f32,
43 pub parts: PartSet,
44 pub offset: FrameTime,
45 pub has_undos: bool,
46 pub has_redos: bool,
47}
48
49#[derive(Clone, Debug)]
50pub enum GuiCommand {
51 StateSnapshot(EngineStateSnapshot),
52 AddLooper(u32, LooperState),
53 AddLooperWithSamples(u32, u64, Box<Waveform>, LooperState),
54 RemoveLooper(u32),
55 ClearLooper(u32),
56
57 LooperStateChange(u32, LooperState),
58
59 AddNewSample(u32, FrameTime, [f32; 2], u64),
60 AddOverdubSample(u32, FrameTime, [f32; 2]),
61 SetLoopLengthAndOffset(u32, u64, FrameTime),
62 UpdateLooperWithSamples(u32, u64, Box<Waveform>, LooperState),
63
64 AddLoopTrigger(u32, FrameTime, LooperCommand),
65 AddGlobalTrigger(FrameTime, Command),
66}
67
68#[derive(Clone)]
69pub enum LogLevel {
70 Info,
71 Warn,
72 Error,
73}
74
75#[derive(Clone)]
76pub struct LogMessage {
77 buffer: ArrayVec<u8, 256>,
78 len: usize,
79 level: LogLevel,
80}
81
82impl LogMessage {
83 pub fn new() -> Self {
84 LogMessage {
85 buffer: ArrayVec::new(),
86 len: 0,
87 level: LogLevel::Info,
88 }
89 }
90
91 pub fn error() -> Self {
92 LogMessage {
93 buffer: ArrayVec::new(),
94 len: 0,
95 level: LogLevel::Error,
96 }
97 }
98
99 pub fn as_str(&self) -> Cow<'_, str> {
100 String::from_utf8_lossy(&self.buffer[0..self.len])
101 }
102}
103
104impl Write for LogMessage {
105 fn write(&mut self, s: &[u8]) -> io::Result<usize> {
106 self.len += s.len();
107 self.buffer.write(s)
108 }
109
110 fn flush(&mut self) -> io::Result<()> {
111 Ok(())
112 }
113}
114
115pub struct GuiSender {
116 cmd_channel: Option<Sender<GuiCommand>>,
117 cur_message: LogMessage,
118 log_channel: Option<Sender<LogMessage>>,
119}
120
121pub struct GuiReceiver {
122 pub cmd_channel: Receiver<GuiCommand>,
123 pub log_channel: Receiver<LogMessage>,
124}
125
126impl GuiSender {
127 pub fn new() -> (GuiSender, GuiReceiver) {
128 let (tx, rx) = bounded(100);
129 let (log_tx, log_rx) = bounded(10);
130
131 let sender = GuiSender {
132 cmd_channel: Some(tx),
133 cur_message: LogMessage::new(),
134 log_channel: Some(log_tx),
135 };
136
137 let receiver = GuiReceiver {
138 cmd_channel: rx,
139 log_channel: log_rx,
140 };
141
142 (sender, receiver)
143 }
144
145 pub fn disconnected() -> GuiSender {
146 GuiSender {
147 cmd_channel: None,
148 cur_message: LogMessage::new(),
149 log_channel: None,
150 }
151 }
152
153 pub fn send_update(&mut self, cmd: GuiCommand) {
154 if let Some(gui_sender) = &self.cmd_channel {
155 match gui_sender.try_send(cmd) {
156 Ok(_) => {}
157 Err(TrySendError::Full(_)) => {
158 warn!("GUI message queue is full");
159 }
160 Err(TrySendError::Disconnected(_)) => {
161 }
163 }
164 }
165 }
166
167 pub fn send_log(&mut self, message: LogMessage) -> () {
168 if let Err(e) = self.send_log_with_result(message) {
169 warn!("Failed to send message to gui: {}", e);
170 }
171 }
172
173 fn send_log_with_result(&mut self, message: LogMessage) -> io::Result<()> {
174 if let Some(log_channel) = &self.log_channel {
175 log_channel.try_send(message).map_err(|e| match e {
176 TrySendError::Full(_) => io::Error::new(ErrorKind::WouldBlock, "queue full"),
177 TrySendError::Disconnected(_) => {
178 io::Error::new(ErrorKind::BrokenPipe, "queue disconnected")
179 }
180 })?;
181 }
182 Ok(())
183 }
184}
185
186impl Clone for GuiSender {
187 fn clone(&self) -> Self {
188 GuiSender {
189 cmd_channel: self.cmd_channel.clone(),
190 cur_message: self.cur_message.clone(),
191 log_channel: self.log_channel.clone(),
192 }
193 }
194}
195
196impl Write for GuiSender {
197 fn write(&mut self, s: &[u8]) -> io::Result<usize> {
198 self.cur_message.len += s.len();
199 self.cur_message.buffer.write(s)
200 }
201
202 fn flush(&mut self) -> io::Result<()> {
203 let message = self.cur_message.clone();
204 self.cur_message.len = 0;
205 self.cur_message.buffer.clear();
206 self.send_log_with_result(message)
207 }
208}