1use anyhow::Error;
4use crossbeam_channel::{Receiver, Sender};
5#[cfg(feature = "egui")]
6use eframe::egui::Key;
7#[cfg(feature = "egui")]
8use egui::KeyHandler;
9use ensnare::{
10 orchestration::{AudioSenderFn, ProjectTitle},
11 prelude::*,
12 types::VisualizationQueue,
13 Project,
14};
15use std::{
16 path::PathBuf,
17 sync::{Arc, RwLock},
18};
19
20#[allow(missing_docs)]
21#[derive(Debug)]
22pub enum ProjectServiceInput {
23 AudioReset(SampleRate, u8),
24 FramesNeeded(usize),
25 #[cfg(feature = "egui")]
26 KeyEvent(Key, bool, Option<Key>),
27 Midi(MidiChannel, MidiMessage),
28 NextTimelineDisplayer,
29 ProjectExportToWav(Option<PathBuf>),
30 ProjectLinkControl(Uid, Uid, ControlIndex),
31 ProjectLoad(PathBuf),
32 ProjectNew,
33 ProjectPlay,
34 ProjectRemoveEntity(Uid),
35 ProjectSave(Option<PathBuf>),
36 ProjectSetSampleRate(SampleRate),
37 ProjectStop,
38 ServiceInit,
39 ServiceQuit,
40 TrackAddEntity(TrackUid, EntityKey),
41 TrackNewAudio,
42 TrackNewAux,
43 TrackNewMidi,
44 VisualizationQueue(VisualizationQueue),
45}
46
47#[allow(missing_docs)]
48#[derive(Debug)]
49pub enum ProjectServiceEvent {
50 ExportFailed(Error),
51 Exported(PathBuf),
52 IsPerformingChanged(bool),
53 LoadFailed(PathBuf, Error),
54 Loaded(Arc<RwLock<Project>>), Midi(MidiChannel, MidiMessage), Quit,
57 SaveFailed(Error),
58 Saved(PathBuf),
59 TitleChanged(ProjectTitle),
60}
61
62#[derive(Debug)]
64pub struct ProjectService {
65 inputs: CrossbeamChannel<ProjectServiceInput>,
66 events: CrossbeamChannel<ProjectServiceEvent>,
67
68 factory: Arc<EntityFactory<dyn Entity>>,
69}
70impl ProvidesService<ProjectServiceInput, ProjectServiceEvent> for ProjectService {
71 fn sender(&self) -> &Sender<ProjectServiceInput> {
72 &self.inputs.sender
73 }
74
75 fn receiver(&self) -> &Receiver<ProjectServiceEvent> {
76 &self.events.receiver
77 }
78}
79impl ProjectService {
80 #[allow(missing_docs)]
81 pub fn new_with(
82 factory: &Arc<EntityFactory<dyn Entity>>,
83 audio_sender_fn: AudioSenderFn,
84 ) -> Self {
85 let r = Self {
86 inputs: Default::default(),
87 events: Default::default(),
88 factory: Arc::clone(factory),
89 };
90 r.spawn_thread(audio_sender_fn);
91 let _ = r.sender().send(ProjectServiceInput::ServiceInit);
92 r
93 }
94
95 fn spawn_thread(&self, audio_sender_fn: AudioSenderFn) {
96 let receiver = self.inputs.receiver.clone();
97 let sender = self.events.sender.clone();
98 let factory = Arc::clone(&self.factory);
99 std::thread::spawn(move || {
100 let mut daemon =
101 ProjectServiceDaemon::new_with(receiver, sender, factory, audio_sender_fn);
102 daemon.execute();
103 });
104 }
105}
106
107struct ProjectServiceDaemon {
108 receiver: Receiver<ProjectServiceInput>,
109 sender: Sender<ProjectServiceEvent>,
110 factory: Arc<EntityFactory<dyn Entity>>,
111
112 project: Arc<RwLock<Project>>,
113
114 #[cfg(feature = "egui")]
115 key_handler: KeyHandler,
116
117 visualization_queue: Option<VisualizationQueue>,
118}
119impl ProjectServiceDaemon {
120 pub fn new_with(
121 receiver: Receiver<ProjectServiceInput>,
122 sender: Sender<ProjectServiceEvent>,
123 factory: Arc<EntityFactory<dyn Entity>>,
124 audio_sender_fn: AudioSenderFn,
125 ) -> Self {
126 let mut project = Project::new_project();
127 project.set_audio_service_sender_fn(audio_sender_fn);
128 Self {
129 receiver,
130 sender,
131 factory,
132 project: Arc::new(RwLock::new(project)),
133 #[cfg(feature = "egui")]
134 key_handler: Default::default(),
135 visualization_queue: Default::default(),
136 }
137 }
138
139 fn notify_new_project(&self) {
140 let _ = self
141 .sender
142 .send(ProjectServiceEvent::Loaded(Arc::clone(&self.project)));
143 }
144
145 fn set_up_new_project(&self, new_project: &mut Project) {
146 if let Some(queue) = self.visualization_queue.as_ref() {
147 new_project.e.visualization_queue = Some(queue.clone());
148 }
149 }
150
151 fn swap_project(&mut self, mut new_project: Project) {
152 self.set_up_new_project(&mut new_project);
153 self.project = Arc::new(RwLock::new(new_project));
154 self.notify_new_project();
155 }
156
157 fn execute(&mut self) {
158 while let Ok(input) = self.receiver.recv() {
159 match input {
160 ProjectServiceInput::ServiceInit => {
161 self.notify_new_project();
162 }
163 ProjectServiceInput::ProjectNew => {
164 let new_project = Project::new_project();
166 self.swap_project(new_project);
167 }
168 ProjectServiceInput::ProjectLoad(path) => match Project::load(path.clone()) {
169 Ok(new_project) => {
170 self.swap_project(new_project);
171 }
172 Err(e) => {
173 let _ = self.sender.send(ProjectServiceEvent::LoadFailed(path, e));
174 }
175 },
176 ProjectServiceInput::ProjectSave(path) => {
177 let mut project = self.project.write().unwrap();
178 match project.save(path) {
179 Ok(save_path) => {
180 let _ = self.sender.send(ProjectServiceEvent::Saved(save_path));
181 }
182 Err(e) => {
183 let _ = self.sender.send(ProjectServiceEvent::SaveFailed(e));
184 }
185 }
186 }
187 ProjectServiceInput::ServiceQuit => {
188 eprintln!("ProjectServiceInput::Quit");
189 let _ = self.sender.send(ProjectServiceEvent::Quit);
190 break;
191 }
192 ProjectServiceInput::ProjectSetSampleRate(sample_rate) => {
193 self.project
194 .write()
195 .unwrap()
196 .update_sample_rate(sample_rate);
197 }
198 ProjectServiceInput::ProjectPlay => {
199 self.project.write().unwrap().play();
200 let _ = self
201 .sender
202 .send(ProjectServiceEvent::IsPerformingChanged(true));
203 }
204 ProjectServiceInput::ProjectStop => {
205 self.project.write().unwrap().stop();
206 let _ = self
207 .sender
208 .send(ProjectServiceEvent::IsPerformingChanged(false));
209 }
210 ProjectServiceInput::TrackAddEntity(track_uid, key) => {
211 if let Ok(mut project) = self.project.write() {
212 let uid = project.mint_entity_uid();
213 if let Some(entity) = self.factory.new_entity(&key, uid) {
214 let _ = project.add_entity(track_uid, entity);
215 } else {
216 eprintln!("ProjectServiceInput::TrackAddEntity failed");
217 }
218 }
219 }
220 ProjectServiceInput::ProjectLinkControl(source_uid, target_uid, index) => {
221 let _ = self
222 .project
223 .write()
224 .unwrap()
225 .link(source_uid, target_uid, index);
226 }
227 #[cfg(feature = "egui")]
228 ProjectServiceInput::KeyEvent(key, pressed, _physical_key) => {
229 if let Some(message) = self.key_handler.handle_key(&key, pressed) {
230 self.project.write().unwrap().handle_midi_message(
231 MidiChannel::default(),
232 message,
233 &mut |c, m| {
234 eprintln!("TODO: {c:?} {m:?}");
235 },
236 )
237 }
238 }
239 ProjectServiceInput::NextTimelineDisplayer => {
240 if let Ok(mut project) = self.project.write() {
241 let selected_track_uids = project.view_state.track_selection_set.clone();
242 selected_track_uids
243 .iter()
244 .for_each(|track_uid| project.advance_track_view_mode(*track_uid));
245 }
246 }
247 ProjectServiceInput::VisualizationQueue(queue) => {
248 self.visualization_queue = Some(queue.clone());
249 self.project.write().unwrap().e.visualization_queue = Some(queue)
250 }
251 ProjectServiceInput::Midi(channel, message) => self
252 .project
253 .write()
254 .unwrap()
255 .handle_midi_message(channel, message, &mut |c, m| {
256 eprintln!("TODO: {c:?} {m:?}");
257 }),
258 ProjectServiceInput::ProjectRemoveEntity(uid) => {
259 let _ = self.project.write().unwrap().remove_entity(uid);
260 }
261 ProjectServiceInput::TrackNewAudio => {
262 let _ = self.project.write().unwrap().new_audio_track();
263 }
264 ProjectServiceInput::TrackNewAux => {
265 let _ = self.project.write().unwrap().new_aux_track();
266 }
267 ProjectServiceInput::TrackNewMidi => {
268 let _ = self.project.write().unwrap().new_midi_track();
269 }
270 ProjectServiceInput::ProjectExportToWav(path) => {
271 let path = path.unwrap_or(PathBuf::from("exported-project.wav"));
272 let _ = self.project.write().unwrap().export_to_wav(path);
273 }
274 ProjectServiceInput::FramesNeeded(count) => {
275 self.project.write().unwrap().generate_and_dispatch_audio(
276 count,
277 Some(&mut |c, m| {
278 let _ = self.sender.send(ProjectServiceEvent::Midi(c, m));
285 }),
286 );
287 }
288 ProjectServiceInput::AudioReset(sample_rate, _channel_count) => {
289 self.project
290 .write()
291 .unwrap()
292 .update_sample_rate(sample_rate);
293 }
294 }
295 }
296 eprintln!("ProjectServiceDaemon exit");
297 }
298}
299
300#[cfg(feature = "egui")]
301pub(super) mod egui {
302 use super::*;
303 use derivative::Derivative;
304 use synonym::Synonym;
305
306 #[derive(Synonym, Derivative)]
312 #[derivative(Default)]
313 #[synonym(skip(Default))]
314 pub(super) struct Octave(#[derivative(Default(value = "5"))] pub u8);
315 impl Octave {
316 fn decrease(&mut self) {
317 if self.0 > 0 {
318 self.0 -= 1;
319 }
320 }
321 fn increase(&mut self) {
322 if self.0 < 10 {
323 self.0 += 1;
324 }
325 }
326 }
327
328 #[derive(Debug, Default)]
332 pub(super) struct KeyHandler {
333 octave: Octave,
334 }
335
336 impl KeyHandler {
337 pub fn handle_key(&mut self, key: &Key, pressed: bool) -> Option<MidiMessage> {
338 match key {
339 Key::A => Some(self.midi_note_message(0, pressed)),
340 Key::W => Some(self.midi_note_message(1, pressed)),
341 Key::S => Some(self.midi_note_message(2, pressed)),
342 Key::E => Some(self.midi_note_message(3, pressed)),
343 Key::D => Some(self.midi_note_message(4, pressed)),
344 Key::F => Some(self.midi_note_message(5, pressed)),
345 Key::T => Some(self.midi_note_message(6, pressed)),
346 Key::G => Some(self.midi_note_message(7, pressed)),
347 Key::Y => Some(self.midi_note_message(8, pressed)),
348 Key::H => Some(self.midi_note_message(9, pressed)),
349 Key::U => Some(self.midi_note_message(10, pressed)),
350 Key::J => Some(self.midi_note_message(11, pressed)),
351 Key::K => Some(self.midi_note_message(12, pressed)),
352 Key::O => Some(self.midi_note_message(13, pressed)),
353 Key::ArrowLeft => {
354 if pressed {
355 self.octave.decrease();
356 }
357 None
358 }
359 Key::ArrowRight => {
360 if pressed {
361 self.octave.increase();
362 }
363 None
364 }
365 _ => None,
366 }
367 }
368
369 fn midi_note_message(&self, midi_note_number: u8, pressed: bool) -> MidiMessage {
370 let midi_note_number = (midi_note_number + self.octave.0 * 12).min(127);
371
372 if pressed {
373 MidiMessage::NoteOn {
374 key: u7::from(midi_note_number),
375 vel: u7::from(127),
376 }
377 } else {
378 MidiMessage::NoteOff {
379 key: u7::from(midi_note_number),
380 vel: u7::from(0),
381 }
382 }
383 }
384 }
385
386 #[cfg(test)]
387 mod tests {
388 use super::*;
389
390 #[test]
391 fn expected_messages_for_keystrokes() {
392 let mut k = KeyHandler::default();
393 let message = k.handle_key(&Key::A, true).unwrap();
394 assert_eq!(
395 message,
396 MidiMessage::NoteOn {
397 key: u7::from(MidiNote::C4 as u8),
398 vel: u7::from(127)
399 }
400 );
401 }
402
403 #[test]
404 fn octaves() {
405 let mut k = KeyHandler::default();
406
407 let message = k.handle_key(&Key::A, true).unwrap();
409 let _ = k.handle_key(&Key::A, false).unwrap();
410 assert_eq!(
411 message,
412 MidiMessage::NoteOn {
413 key: u7::from(MidiNote::C4 as u8),
414 vel: u7::from(127)
415 }
416 );
417
418 let _ = k.handle_key(&Key::ArrowRight, true);
420 let message = k.handle_key(&Key::A, true).unwrap();
421 let _ = k.handle_key(&Key::A, false).unwrap();
422 assert_eq!(
423 message,
424 MidiMessage::NoteOn {
425 key: u7::from(MidiNote::C5 as u8),
426 vel: u7::from(127)
427 }
428 );
429
430 let _ = k.handle_key(&Key::ArrowRight, true);
432 let _ = k.handle_key(&Key::ArrowRight, true);
433 let _ = k.handle_key(&Key::ArrowRight, true);
434 let _ = k.handle_key(&Key::ArrowRight, true);
435 let message = k.handle_key(&Key::A, true).unwrap();
436 let _ = k.handle_key(&Key::A, false).unwrap();
437 assert_eq!(
438 message,
439 MidiMessage::NoteOn {
440 key: u7::from(MidiNote::C9 as u8),
441 vel: u7::from(127)
442 }
443 );
444
445 let _ = k.handle_key(&Key::ArrowRight, true);
446 let message = k.handle_key(&Key::A, true).unwrap();
447 let _ = k.handle_key(&Key::A, false).unwrap();
448 assert_eq!(
449 message,
450 MidiMessage::NoteOn {
451 key: u7::from(MidiNote::C9 as u8),
452 vel: u7::from(127)
453 },
454 "Trying to go higher than max octave shouldn't change anything."
455 );
456
457 let mut k = KeyHandler::default();
459 let _ = k.handle_key(&Key::ArrowLeft, true);
460 let message = k.handle_key(&Key::A, true).unwrap();
461 let _ = k.handle_key(&Key::A, false).unwrap();
462 assert_eq!(
463 message,
464 MidiMessage::NoteOn {
465 key: u7::from(MidiNote::C3 as u8),
466 vel: u7::from(127)
467 }
468 );
469 let _ = k.handle_key(&Key::ArrowLeft, true);
470 let message = k.handle_key(&Key::A, true).unwrap();
471 let _ = k.handle_key(&Key::A, false).unwrap();
472 assert_eq!(
473 message,
474 MidiMessage::NoteOn {
475 key: u7::from(MidiNote::C2 as u8),
476 vel: u7::from(127)
477 }
478 );
479 let _ = k.handle_key(&Key::ArrowLeft, true);
480 let message = k.handle_key(&Key::A, true).unwrap();
481 let _ = k.handle_key(&Key::A, false).unwrap();
482 assert_eq!(
483 message,
484 MidiMessage::NoteOn {
485 key: u7::from(MidiNote::C1 as u8),
486 vel: u7::from(127)
487 }
488 );
489 let _ = k.handle_key(&Key::ArrowLeft, true);
490 let message = k.handle_key(&Key::A, true).unwrap();
491 let _ = k.handle_key(&Key::A, false).unwrap();
492 assert_eq!(
493 message,
494 MidiMessage::NoteOn {
495 key: u7::from(MidiNote::C0 as u8),
496 vel: u7::from(127)
497 }
498 );
499 let _ = k.handle_key(&Key::ArrowLeft, true);
500 let message = k.handle_key(&Key::A, true).unwrap();
501 let _ = k.handle_key(&Key::A, false).unwrap();
502 assert_eq!(
503 message,
504 MidiMessage::NoteOn {
505 key: u7::from(MidiNote::CSub0 as u8),
506 vel: u7::from(127)
507 }
508 );
509 let _ = k.handle_key(&Key::ArrowLeft, true);
510 let message = k.handle_key(&Key::A, true).unwrap();
511 let _ = k.handle_key(&Key::A, false).unwrap();
512 assert_eq!(
513 message,
514 MidiMessage::NoteOn {
515 key: u7::from(MidiNote::CSub0 as u8),
516 vel: u7::from(127)
517 },
518 "Trying to go below the lowest octave should stay at lowest octave."
519 );
520 }
521 }
522}