loopers_common/
gui_channel.rs

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                    // we're shutting down
162                }
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}