klavier_jack/
player.rs

1use std::{
2    fmt::Display,
3    marker::PhantomData,
4    rc::Rc,
5    sync::mpsc::{sync_channel, Receiver, RecvError, SyncSender},
6    thread::{self},
7    time,
8};
9
10use error_stack::Report;
11use jack::{Client, ClientStatus, Control, MidiOut, ProcessScope, RawMidi};
12use klavier_core::{midi_events::{MidiEvents, PlayData, create_midi_events}, play_start_tick::ToAccumTickError, tempo::TempoValue};
13use klavier_core::play_start_tick::PlayStartTick;
14use klavier_core::{
15    bar::Bar,
16    ctrl_chg::CtrlChg,
17    duration::Duration,
18    global_repeat::RenderRegionWarning,
19    key::Key,
20    note::Note,
21    project::ModelChangeMetadata,
22    repeat::{RenderRegionError, AccumTick},
23    rhythm::Rhythm,
24    tempo::Tempo,
25};
26use klavier_helper::{bag_store::BagStore, store::{Store}};
27
28#[cfg(not(test))]
29use jack::MidiWriter;
30
31pub struct Player {
32    // Frequency (ex. 48kHz => 48000)
33    pub sampling_rate: u32,
34    cmd_channel: SyncSender<Cmd>,
35    resp_channel: Option<Receiver<Resp>>,
36    closer: Option<Box<dyn Send + 'static + FnOnce() -> Option<jack::Error>>>,
37}
38
39#[derive(Clone)]
40pub enum Cmd {
41    Play {
42        seq: usize,
43        play_data: PlayData,
44        start_cycle: u64,
45    },
46    Stop {
47        seq: usize,
48    },
49}
50
51#[derive(Debug, PartialEq, Eq, Clone)]
52pub enum CmdError {
53    AlreadyPlaying,
54    MidiWriteError(String),
55}
56
57#[derive(Debug, PartialEq, Eq, Clone)]
58pub enum CmdInfo {
59    PlayingEnded,
60    CurrentLoc {
61        seq: usize,
62        tick: u32,
63        accum_tick: AccumTick,
64    },
65}
66
67#[derive(Debug, PartialEq, Eq, Clone)]
68pub enum Resp {
69    Err { seq: Option<usize>, error: CmdError },
70    Info { seq: Option<usize>, info: CmdInfo },
71    Ok { seq: usize },
72}
73
74pub enum PlayState {
75    Init,
76    Playing {
77        seq: usize,
78        current_loc: u64,
79        play_data: PlayData,
80    },
81    Stopping,
82}
83
84#[cfg(not(test))]
85pub struct JackClientProxy {
86    client: Option<Client>,
87    status: ClientStatus,
88}
89
90pub struct TestMidiWriter<'a> {
91    _phantom: PhantomData<&'a ()>,
92}
93
94impl<'a> TestMidiWriter<'a> {
95    pub fn write(&mut self, _message: &RawMidi) -> core::result::Result<(), jack::Error> {
96        Ok(())
97    }
98}
99
100#[cfg(not(test))]
101impl JackClientProxy {
102    pub fn new(app_name: &str, options: jack::ClientOptions) -> core::result::Result<Self, jack::Error> {
103        let (client, status) = jack::Client::new(app_name, options)?;
104        Ok(Self {
105            client: Some(client),
106            status,
107        })
108    }
109
110    pub fn buffer_size(&self) -> u32 {
111        self.client.as_ref().map(|c| c.buffer_size()).unwrap()
112    }
113
114    pub fn sampling_rate(&self) -> u32 {
115        self.client.as_ref().map(|c| c.sample_rate()).unwrap() as u32
116    }
117
118    pub fn midi_out_port(&self) -> core::result::Result<Port<MidiOut>, jack::Error> {
119        Ok(self
120            .client
121            .as_ref()
122            .map(|c| c.register_port("MIDI OUT", jack::MidiOut::default()))
123            .unwrap()?)
124    }
125
126    pub fn closer<F>(
127        &mut self,
128        callback: F,
129    ) -> core::result::Result<Option<Box<dyn Send + 'static + FnOnce() -> Option<jack::Error>>>, jack::Error>
130    where
131        F: 'static + Send + FnMut(&Client, &ProcessScope) -> Control,
132    {
133        let active_client = {
134            let client = self.client.take().unwrap();
135            client.activate_async((), jack::contrib::ClosureProcessHandler::new(callback))?
136        };
137        let closer = move || active_client.deactivate().err();
138        Ok(Some(Box::new(closer)))
139    }
140}
141
142pub struct TestPort<T> {
143    _phantom: PhantomData<T>,
144}
145
146impl<T> TestPort<T> {
147    pub fn writer<'a>(&'a mut self, _ps: &'a ProcessScope) -> TestMidiWriter<'a> {
148        TestMidiWriter {
149            _phantom: PhantomData,
150        }
151    }
152}
153
154pub struct TestJackClientProxy {
155    pub status: ClientStatus,
156
157    pub app_name: String,
158    pub buffer_size: u32,
159    pub sampling_rate: u32,
160}
161
162#[cfg(not(test))]
163use jack::Port;
164
165impl TestJackClientProxy {
166    pub fn new(_app_name: &str, _options: jack::ClientOptions) -> core::result::Result<Self, jack::Error> {
167        panic!("You need to pass client_factory to Player::open() for testing.");
168    }
169
170    pub fn new_test(
171        app_name: &str,
172        _options: jack::ClientOptions,
173        status: ClientStatus,
174        buffer_size: u32,
175        sampling_rate: u32,
176    ) -> core::result::Result<Self, jack::Error> {
177        Ok(Self {
178            status,
179            app_name: app_name.to_owned(),
180            buffer_size,
181            sampling_rate,
182        })
183    }
184
185    pub fn buffer_size(&self) -> u32 {
186        self.buffer_size
187    }
188
189    pub fn sampling_rate(&self) -> u32 {
190        self.sampling_rate
191    }
192
193    pub fn midi_out_port(&self) -> core::result::Result<TestPort<MidiOut>, jack::Error> {
194        Ok(TestPort {
195            _phantom: PhantomData,
196        })
197    }
198
199    pub fn closer<F>(
200        &mut self,
201        _callback: F,
202    ) -> core::result::Result<Option<Box<dyn Send + 'static + FnOnce() -> Option<jack::Error>>>, jack::Error>
203    where
204        F: 'static + Send + FnMut(&Client, &ProcessScope) -> Control,
205    {
206        thread::spawn(move || {
207            let _cb = _callback;
208            // Keep callback instance until test is finished.
209            thread::sleep(time::Duration::from_secs(60));
210        });
211
212        Ok(None)
213    }
214}
215
216#[cfg(test)]
217use TestJackClientProxy as JCProxy;
218
219#[cfg(not(test))]
220use JackClientProxy as JCProxy;
221
222#[cfg(test)]
223use TestMidiWriter as MW;
224
225#[cfg(not(test))]
226use MidiWriter as MW;
227
228impl Player {
229    fn resp(resp_sender: &SyncSender<Resp>, resp: Resp) {
230        if let Err(e) = resp_sender.send(resp) {
231            println!("Cannot send response: {:?}.", e);
232        }
233    }
234
235    fn perform_cmd(
236        cmd_receiver: &Receiver<Cmd>,
237        pstate: &mut PlayState,
238        resp_sender: &SyncSender<Resp>,
239    ) -> jack::Control {
240        match cmd_receiver.try_recv() {
241            Ok(cmd) => match cmd {
242                Cmd::Play {
243                    seq,
244                    play_data,
245                    start_cycle: start_loc,
246                } => match pstate {
247                    PlayState::Init => {
248                        *pstate = PlayState::Playing {
249                            seq,
250                            current_loc: start_loc,
251                            play_data,
252                        };
253                        Self::resp(&resp_sender, Resp::Ok { seq });
254                    }
255                    PlayState::Playing {
256                        seq: _seq,
257                        current_loc: _,
258                        play_data: _,
259                    } => {
260                        Self::resp(
261                            &resp_sender,
262                            Resp::Err {
263                                seq: Some(seq),
264                                error: CmdError::AlreadyPlaying,
265                            },
266                        );
267                    },
268                    PlayState::Stopping => {
269                        Self::resp(
270                            &resp_sender,
271                            Resp::Err {
272                                seq: Some(seq),
273                                error: CmdError::AlreadyPlaying,
274                            },
275                        );
276                    },
277                },
278                Cmd::Stop { seq } => match pstate {
279                    PlayState::Init => {
280                        Self::resp(&resp_sender, Resp::Ok { seq });
281                    }
282                    PlayState::Playing {
283                        seq: _seq,
284                        current_loc: _,
285                        play_data: _,
286                    } => {
287                        *pstate = PlayState::Stopping;
288                        Self::resp(
289                            &resp_sender,
290                            Resp::Info {
291                                seq: Some(seq),
292                                info: CmdInfo::PlayingEnded,
293                            },
294                        );
295                    },
296                    PlayState::Stopping => {
297                        Self::resp(&resp_sender, Resp::Ok { seq });
298                    },
299                },
300            },
301            Err(err) => {
302                if err == std::sync::mpsc::TryRecvError::Disconnected {
303                    return jack::Control::Quit;
304                }
305            }
306        }
307
308        jack::Control::Continue
309    }
310
311    fn perform_state<'a>(
312        pstate: &mut PlayState,
313        midi_writer: &mut MW<'a>,
314        resp_sender: &SyncSender<Resp>,
315        buffer_size: u32,
316        sampling_rate: u32,
317    ) {
318        match pstate {
319            PlayState::Init => {}
320            PlayState::Playing {
321                seq: playing_seq,
322                current_loc,
323                play_data,
324            } => {
325                let (_idx, data) = play_data
326                    .midi_data
327                    .range(*current_loc..(*current_loc + buffer_size as u64));
328                for (cycle, midi) in data.iter() {
329                    let offset = (cycle - *current_loc) as u32;
330
331                    for bytes in midi.iter() {
332                        if let Err(e) = midi_writer.write(&jack::RawMidi {
333                            time: offset,
334                            bytes,
335                        }) {
336                            Self::resp(
337                                &resp_sender,
338                                Resp::Err {
339                                    seq: None,
340                                    error: CmdError::MidiWriteError(e.to_string()),
341                                },
342                            )
343                        }
344                    }
345                }
346
347                let new_loc = *current_loc + buffer_size as u64;
348                let ended = play_data
349                    .midi_data
350                    .peek_last()
351                    .map(|(cycle, _midi)| *cycle < new_loc)
352                    .unwrap_or(false);
353                if ended {
354                    Self::resp(
355                        &resp_sender,
356                        Resp::Info {
357                            seq: Some(*playing_seq),
358                            info: CmdInfo::PlayingEnded,
359                        },
360                    );
361                    *pstate = PlayState::Init;
362                } else {
363                    let accum_tick = play_data.cycle_to_tick(*current_loc, sampling_rate);
364                    let tick = play_data.accum_tick_to_tick(accum_tick);
365                    Self::resp(
366                        &resp_sender,
367                        Resp::Info {
368                            seq: Some(*playing_seq),
369                            info: CmdInfo::CurrentLoc {
370                                seq: *playing_seq,
371                                tick,
372                                accum_tick,
373                            },
374                        },
375                    );
376                    *current_loc = new_loc;
377                }
378            },
379            PlayState::Stopping => {
380                let mut bytes: Vec<u8> = Vec::with_capacity(3 * 16);
381                for ch in 0..16 {
382                    bytes.push(0xB0 + ch); // Soft off
383                    bytes.push(67);
384                    bytes.push(0);
385                }
386                if let Err(e) = midi_writer.write(&jack::RawMidi { time: 0, bytes: &bytes }) {
387                    Self::resp(
388                        &resp_sender,
389                        Resp::Err {
390                            seq: None,
391                            error: CmdError::MidiWriteError(e.to_string()),
392                        },
393                    )
394                }
395
396                let mut bytes: Vec<u8> = Vec::with_capacity(3 * 16);
397                for ch in 0..16 {
398                    bytes.push(0xB0 + ch); // All sound off
399                    bytes.push(120);
400                    bytes.push(0);
401                }
402                if let Err(e) = midi_writer.write(&jack::RawMidi { time: 0, bytes: &bytes }) {
403                    Self::resp(
404                        &resp_sender,
405                        Resp::Err {
406                            seq: None,
407                            error: CmdError::MidiWriteError(e.to_string()),
408                        },
409                    )
410                }
411
412                let mut bytes: Vec<u8> = Vec::with_capacity(3 * 16);
413                for ch in 0..16 {
414                    bytes.push(0xB0 + ch); // Dumper off
415                    bytes.push(64);
416                    bytes.push(0);
417                }
418                if let Err(e) = midi_writer.write(&jack::RawMidi { time: 0, bytes: &bytes }) {
419                    Self::resp(
420                        &resp_sender,
421                        Resp::Err {
422                            seq: None,
423                            error: CmdError::MidiWriteError(e.to_string()),
424                        },
425                    )
426                }
427
428                *pstate = PlayState::Init;
429            },
430        }
431    }
432
433    pub fn open(
434        app_name: &str,
435        client_factory: Option<
436            Box<dyn FnOnce(&str, jack::ClientOptions) -> core::result::Result<JCProxy, jack::Error>>,
437        >,
438    ) -> core::result::Result<(Self, jack::ClientStatus), Report<jack::Error>> {
439        let mut proxy = match client_factory {
440            Some(factory) => factory(app_name, jack::ClientOptions::empty())?,
441            None => JCProxy::new(app_name, jack::ClientOptions::empty())?,
442        };
443        let (cmd_sender, cmd_receiver) = sync_channel::<Cmd>(64);
444        let (resp_sender, resp_receiver) = sync_channel::<Resp>(64);
445
446        let buffer_size: u32 = proxy.buffer_size();
447        let sampling_rate: u32 = proxy.sampling_rate();
448        let mut state = PlayState::Init;
449        let proxy_status = proxy.status;
450
451        let mut midi_out_port = proxy.midi_out_port()?;
452        let callback = move |_client: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
453            let mut pstate = &mut state;
454            let mut midi_writer = midi_out_port.writer(ps);
455
456            if Self::perform_cmd(&cmd_receiver, &mut pstate, &resp_sender) == jack::Control::Quit {
457                return jack::Control::Quit;
458            }
459
460            Self::perform_state(
461                &mut pstate,
462                &mut midi_writer,
463                &resp_sender,
464                buffer_size,
465                sampling_rate,
466            );
467
468            jack::Control::Continue
469        };
470
471        let closer: Option<Box<dyn Send + 'static + FnOnce() -> Option<jack::Error>>> =
472            proxy.closer(callback)?;
473
474        Ok((
475            Player {
476                sampling_rate,
477                cmd_channel: cmd_sender,
478                resp_channel: Some(resp_receiver),
479                closer,
480            },
481            proxy_status,
482        ))
483    }
484
485    pub fn play(
486        &mut self,
487        seq: usize,
488        play_start_loc: Option<PlayStartTick>,
489        top_rhythm: Rhythm,
490        top_key: Key,
491        note_repo: &BagStore<u32, Rc<Note>, ModelChangeMetadata>,
492        bar_repo: &Store<u32, Bar, ModelChangeMetadata>,
493        tempo_repo: &Store<u32, Tempo, ModelChangeMetadata>,
494        dumper_repo: &Store<u32, CtrlChg, ModelChangeMetadata>,
495        soft_repo: &Store<u32, CtrlChg, ModelChangeMetadata>,
496    ) -> core::result::Result<Vec<RenderRegionWarning>, Report<PlayError>> {
497        let (events, warnings): (MidiEvents, Vec<RenderRegionWarning>) = create_midi_events(
498            top_rhythm,
499            top_key,
500            note_repo,
501            bar_repo,
502            tempo_repo,
503            dumper_repo,
504            soft_repo,
505        )
506        .map_err(|e| PlayError::RenderError(e.current_context().clone()))?;
507
508        let cycles_by_tick: Store<u32, (TempoValue, u64), ()>
509          = events.cycles_by_accum_tick(self.sampling_rate, Duration::TICK_RESOLUTION as u32);
510        let start_accum_tick: u32 = match play_start_loc {
511            None => 0,
512            Some(loc) => {
513                match loc.to_accum_tick(&events.chunks) {
514                    Ok(tick) => tick,
515                    Err(err) => Err(PlayError::PlayStartTickError(err))?,
516                }
517            }
518        };
519        let start_cycle: u64 = MidiEvents::accum_tick_to_cycle(
520            &cycles_by_tick, start_accum_tick, self.sampling_rate, Duration::TICK_RESOLUTION as u32
521        );
522        let play_data: PlayData = events.to_play_data(cycles_by_tick, self.sampling_rate, Duration::TICK_RESOLUTION as u32);
523
524        self.cmd_channel
525            .send(Cmd::Play { seq, play_data, start_cycle })
526            .map_err(|_e| PlayError::SendError { seq })?;
527        Ok(warnings)
528    }
529
530    pub fn stop(&mut self, seq: usize) -> core::result::Result<(), Report<PlayError>> {
531        self.cmd_channel
532            .send(Cmd::Stop { seq })
533            .map_err(|_e| PlayError::SendError { seq })?;
534        Ok(())
535    }
536
537    /// Get response from the response receiver.
538    pub fn get_resp(&self) -> core::result::Result<Resp, RecvError> {
539        match &self.resp_channel {
540            Some(resp) => Ok(resp.recv()?),
541            None => {
542                panic!("Once you call the take_resp(), you need to receive responses by yourself!")
543            }
544        }
545    }
546
547    /// Take the response receiver. Once you take the receiver, you cannot call get_resp() any more. You need to receive responses by yourself through the taken receiver.
548    /// If you have already taken the receiver before, None will return.
549    pub fn take_resp(&mut self) -> Option<Receiver<Resp>> {
550        self.resp_channel.take()
551    }
552}
553
554impl Drop for Player {
555    fn drop(&mut self) {
556        if let Some(f) = self.closer.take() {
557            let handle = thread::spawn(|| f());
558            for _ in 0..50 {
559                if handle.is_finished() {
560                    break;
561                }
562                thread::sleep(time::Duration::from_millis(100));
563            }
564        }
565    }
566}
567
568#[derive(Debug, Clone, PartialEq)]
569pub enum PlayError {
570    RenderError(RenderRegionError),
571    SendError { seq: usize },
572    PlayStartTickError(ToAccumTickError),
573}
574
575impl Display for PlayError {
576    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
577        write!(f, "{:?}", self)
578    }
579}
580
581impl std::error::Error for PlayError {}
582
583// pub struct TestPlayer {
584//   pub sampling_rate: usize,
585//   pub cmd_channel: SyncSender<Cmd>,
586//   pub resp_channel: Option<Receiver<Resp>>,
587
588//   pub cmd_receiver: Receiver<Cmd>,
589//   pub resp_sender: SyncSender<Resp>,
590//   pub name: String,
591// }
592
593// impl TestPlayer {
594//   pub const SAMPLING_RATE: Cell<usize> = Cell::new(48000);
595//   pub const CLIENT_STATUS: Cell<jack::ClientStatus> = Cell::new(jack::ClientStatus::empty());
596
597//   pub fn open(
598//     app_name: &str,
599//     _client_factory: Option<Box<impl FnOnce(&str, jack::ClientOptions) -> Result<(Client, ClientStatus), jack::Error>>>
600//   ) -> Result<(Self, jack::ClientStatus), jack::Error> {
601//     let (cmd_sender, cmd_receiver) = sync_channel::<Cmd>(256);
602//     let (resp_sender, resp_receiver) = sync_channel::<Resp>(256);
603
604//     Ok((
605//       Self {
606//         sampling_rate: Self::SAMPLING_RATE.get(),
607//         cmd_channel: cmd_sender,
608//         resp_channel: Some(resp_receiver),
609//         cmd_receiver,
610//         resp_sender,
611//         name: app_name.to_owned(),
612//       },
613//       Self::CLIENT_STATUS.get()
614//     ))
615//   }
616
617//   pub fn play(
618//     &mut self,
619//     seq: usize, top_rhythm: Rhythm, top_key: Key,
620//     note_repo: &BagStore<u32, Rc<Note>, ModelChangeMetadata>,
621//     bar_repo: &Store<u32, Bar, ModelChangeMetadata>,
622//     tempo_repo: &Store<u32, Tempo, ModelChangeMetadata>,
623//     dumper_repo: &Store<u32, CtrlChg, ModelChangeMetadata>,
624//     soft_repo: &Store<u32, CtrlChg, ModelChangeMetadata>,
625//   ) -> Result <Vec<RenderRegionWarning>, PlayError> {
626//     let (events, warnings) = Player::create_midi_events(
627//       top_rhythm, top_key, note_repo, bar_repo, tempo_repo, dumper_repo, soft_repo
628//     ).map_err(|e| PlayError::RenderError(e.current_context().clone()))?;
629//     let play_data = events.to_play_data(self.sampling_rate, Duration::TICK_RESOLUTION as u32);
630//     self.cmd_channel.send(Cmd::Play { seq, play_data }).map_err(|_e| PlayError::SendError { seq })?;
631//     Ok(warnings)
632//   }
633
634//   pub fn stop(&mut self, seq: usize) -> Result <(), PlayError> {
635//     self.cmd_channel.send(Cmd::Stop { seq }).map_err(|_e| PlayError::SendError { seq })?;
636//     Ok(())
637//   }
638
639//   pub fn get_resp(&self) -> Result<Resp, RecvError> {
640//     match &self.resp_channel {
641//         Some(resp) => Ok(resp.recv()?),
642//         None => panic!("Once you call the take_resp(), you need to receive responses by yourself!"),
643//     }
644//   }
645
646//   pub fn take_resp(&mut self) -> Option<Receiver<Resp>> {
647//     self.resp_channel.take()
648//   }
649// }
650
651#[cfg(test)]
652mod tests {
653    use super::{Cmd, PlayState, Player, Resp};
654    use crate::player::TestJackClientProxy;
655    use crate::player::{CmdError, CmdInfo};
656    use jack::ClientStatus;
657    use klavier_core::duration::Duration;
658    use klavier_core::midi_events::create_midi_events;
659    use klavier_core::{
660        key::Key,
661        note::Note,
662        octave::Octave,
663        pitch::Pitch,
664        project::ModelChangeMetadata,
665        rhythm::Rhythm,
666        sharp_flat::SharpFlat,
667        solfa::Solfa,
668    };
669    use klavier_helper::{bag_store::BagStore, store::Store};
670    use std::{rc::Rc, sync::mpsc::sync_channel};
671
672    #[test]
673    fn play_send_cmd() {
674        let mut note_repo = BagStore::new(false);
675        let note0 = Rc::new(Note {
676            base_start_tick: 100,
677            pitch: Pitch::new(Solfa::F, Octave::Oct4, SharpFlat::Null),
678            ..Default::default()
679        });
680        note_repo.add(
681            note0.start_tick(),
682            note0.clone(),
683            ModelChangeMetadata::default()
684        );
685
686        let (events, _warnings) = create_midi_events(
687            Rhythm::new(2, 4),
688            Key::SHARP_1,
689            &note_repo,
690            &Store::new(false),
691            &Store::new(false),
692            &Store::new(false),
693            &Store::new(false),
694        )
695        .unwrap();
696
697        let cycles_by_tick = events.cycles_by_accum_tick(48000, Duration::TICK_RESOLUTION as u32);
698        let play_data0 = events.to_play_data(cycles_by_tick, 48000, 240);
699
700        let factory = Box::new(|app_name: &str, _options| {
701            Ok(TestJackClientProxy {
702                status: ClientStatus::empty(),
703                app_name: app_name.to_owned(),
704                buffer_size: 2048,
705                sampling_rate: 48000,
706            })
707        });
708        let (mut player, _status) = Player::open("my player", Some(factory)).unwrap();
709        let (cmd_sender, cmd_receiver) = sync_channel::<Cmd>(64);
710
711        player.cmd_channel = cmd_sender;
712
713        let _warnings = player
714            .play(
715                1,
716                None,
717                Rhythm::new(2, 4),
718                Key::SHARP_1,
719                &note_repo,
720                &Store::new(false),
721                &Store::new(false),
722                &Store::new(false),
723                &Store::new(false),
724            )
725            .unwrap();
726
727        let cmd = cmd_receiver.recv().unwrap();
728
729        match cmd {
730            Cmd::Play { seq, play_data, start_cycle: _ } => {
731                assert_eq!(seq, 1);
732
733                let midi_data0: Vec<&(u64, Vec<Vec<u8>>)> = play_data0.midi_data.iter().collect();
734                let midi_data1: Vec<&(u64, Vec<Vec<u8>>)> = play_data.midi_data.iter().collect();
735                assert_eq!(midi_data0, midi_data1);
736            }
737            Cmd::Stop { seq: _ } => panic!("test failed"),
738        };
739    }
740
741    #[test]
742    fn play_cmd_change_state() {
743        let mut note_repo = BagStore::new(false);
744        let note0 = Rc::new(Note {
745            base_start_tick: 100,
746            pitch: Pitch::new(Solfa::F, Octave::Oct4, SharpFlat::Null),
747            ..Default::default()
748        });
749        note_repo.add(
750            note0.start_tick(),
751            note0.clone(),
752            ModelChangeMetadata::default(),
753        );
754
755        let (events, _warnings) = create_midi_events(
756            Rhythm::new(2, 4),
757            Key::SHARP_1,
758            &note_repo,
759            &Store::new(false),
760            &Store::new(false),
761            &Store::new(false),
762            &Store::new(false),
763        )
764        .unwrap();
765
766        let cycles_by_tick = events.cycles_by_accum_tick(48000, Duration::TICK_RESOLUTION as u32);
767        let play_data0 = events.to_play_data(cycles_by_tick, 48000, 240);
768        let factory = Box::new(|app_name: &str, _options| {
769            Ok(TestJackClientProxy {
770                status: ClientStatus::empty(),
771                app_name: app_name.to_owned(),
772                buffer_size: 2048,
773                sampling_rate: 48000,
774            })
775        });
776        let (mut player, _status) = Player::open("my player", Some(factory)).unwrap();
777        let (cmd_sender, cmd_receiver) = sync_channel::<Cmd>(64);
778        let (resp_sender, resp_receiver) = sync_channel::<Resp>(64);
779
780        player.cmd_channel = cmd_sender;
781
782        let _warnings = player
783            .play(
784                1,
785                None,
786                Rhythm::new(2, 4),
787                Key::SHARP_1,
788                &note_repo,
789                &Store::new(false),
790                &Store::new(false),
791                &Store::new(false),
792                &Store::new(false),
793            )
794            .unwrap();
795
796        let mut state = PlayState::Init;
797        let ctrl = Player::perform_cmd(&cmd_receiver, &mut state, &resp_sender);
798        assert_eq!(ctrl, jack::Control::Continue);
799
800        match &state {
801            PlayState::Init => panic!("Should playing."),
802            PlayState::Playing {
803                seq,
804                current_loc,
805                play_data,
806            } => {
807                assert_eq!(*seq, 1);
808                assert_eq!(*current_loc, 0);
809                let midi_data0: Vec<&(u64, Vec<Vec<u8>>)> = play_data0.midi_data.iter().collect();
810                let midi_data1: Vec<&(u64, Vec<Vec<u8>>)> = play_data.midi_data.iter().collect();
811                assert_eq!(midi_data0, midi_data1);
812            },
813            PlayState::Stopping => panic!("Should playing.")
814        }
815        let resp = resp_receiver.recv().unwrap();
816        assert_eq!(resp, Resp::Ok { seq: 1 });
817
818        // Play again while playing.
819        let _warnings = player
820            .play(
821                2,
822                None,
823                Rhythm::new(2, 4),
824                Key::SHARP_1,
825                &note_repo,
826                &Store::new(false),
827                &Store::new(false),
828                &Store::new(false),
829                &Store::new(false),
830            )
831            .unwrap();
832
833        let ctrl = Player::perform_cmd(&cmd_receiver, &mut state, &resp_sender);
834        assert_eq!(ctrl, jack::Control::Continue);
835
836        match &state {
837            PlayState::Init => panic!("Should playing."),
838            PlayState::Playing {
839                seq,
840                current_loc,
841                play_data,
842            } => {
843                assert_eq!(*seq, 1);
844                assert_eq!(*current_loc, 0);
845                let midi_data0: Vec<&(u64, Vec<Vec<u8>>)> = play_data0.midi_data.iter().collect();
846                let midi_data1: Vec<&(u64, Vec<Vec<u8>>)> = play_data.midi_data.iter().collect();
847                assert_eq!(midi_data0, midi_data1);
848            },
849            PlayState::Stopping => panic!("Should playing.")
850        }
851        let resp = resp_receiver.recv().unwrap();
852        assert_eq!(
853            resp,
854            Resp::Err {
855                seq: Some(2),
856                error: CmdError::AlreadyPlaying
857            }
858        );
859
860        // Stop playing.
861        player.stop(3).unwrap();
862        let ctrl = Player::perform_cmd(&cmd_receiver, &mut state, &resp_sender);
863        assert_eq!(ctrl, jack::Control::Continue);
864
865        match &state {
866            PlayState::Init => {}
867            PlayState::Playing {
868                seq: _,
869                current_loc: _,
870                play_data: _,
871            } => panic!("Should init"),
872            PlayState::Stopping => {}
873        }
874        let resp = resp_receiver.recv().unwrap();
875        assert_eq!(
876            resp,
877            Resp::Info {
878                seq: Some(3),
879                info: CmdInfo::PlayingEnded
880            }
881        );
882
883        // Stop again.
884        player.stop(4).unwrap();
885        let ctrl = Player::perform_cmd(&cmd_receiver, &mut state, &resp_sender);
886        assert_eq!(ctrl, jack::Control::Continue);
887
888        match &state {
889            PlayState::Init => {}
890            PlayState::Playing {
891                seq: _,
892                current_loc: _,
893                play_data: _,
894            } => panic!("Should init"),
895            PlayState::Stopping => {}
896        }
897        let resp = resp_receiver.recv().unwrap();
898        assert_eq!(resp, Resp::Ok { seq: 4 });
899
900        drop(player);
901        let ctrl = Player::perform_cmd(&cmd_receiver, &mut state, &resp_sender);
902        assert_eq!(ctrl, jack::Control::Quit);
903    }
904}