devices/virtio/snd/common_backend/
stream_info.rs

1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::fmt;
6use std::rc::Rc;
7use std::sync::atomic::AtomicBool;
8use std::sync::atomic::Ordering;
9use std::sync::Arc;
10use std::time::Duration;
11
12use audio_streams::SampleFormat;
13use audio_streams::StreamEffect;
14use base::error;
15use base::warn;
16use cros_async::sync::Condvar;
17use cros_async::sync::RwLock as AsyncRwLock;
18use cros_async::Executor;
19use futures::channel::mpsc;
20use futures::Future;
21use futures::TryFutureExt;
22use serde::Deserialize;
23use serde::Serialize;
24
25use super::Error;
26use super::PcmResponse;
27use super::WorkerStatus;
28use crate::virtio::snd::common::*;
29use crate::virtio::snd::common_backend::async_funcs::*;
30use crate::virtio::snd::common_backend::DirectionalStream;
31use crate::virtio::snd::common_backend::SysAsyncStreamObjects;
32use crate::virtio::snd::constants::*;
33use crate::virtio::snd::sys::SysAudioStreamSource;
34use crate::virtio::snd::sys::SysAudioStreamSourceGenerator;
35use crate::virtio::DescriptorChain;
36
37/// Parameters for setting parameters in StreamInfo
38#[derive(Copy, Clone, Debug)]
39pub struct SetParams {
40    pub channels: u8,
41    pub format: SampleFormat,
42    pub frame_rate: u32,
43    pub buffer_bytes: usize,
44    pub period_bytes: usize,
45    pub dir: u8,
46}
47
48/// StreamInfoBuilder builds a [`StreamInfo`]. It is used when we want to store the parameters to
49/// create a [`StreamInfo`] beforehand and actually create it later. (as is the case with VirtioSnd)
50///
51/// To create a new StreamInfoBuilder, see [`StreamInfo::builder()`].
52#[derive(Clone)]
53pub struct StreamInfoBuilder {
54    stream_source_generator: Arc<SysAudioStreamSourceGenerator>,
55    effects: Vec<StreamEffect>,
56    card_index: usize,
57    #[cfg(windows)]
58    audio_client_guid: Option<String>,
59}
60
61impl StreamInfoBuilder {
62    /// Creates a StreamInfoBuilder with minimal required fields:
63    ///
64    /// * `stream_source_generator`: Generator which generates stream source in
65    ///   [`StreamInfo::prepare()`].
66    /// * `card_index`: The ALSA card index.
67    pub fn new(
68        stream_source_generator: Arc<SysAudioStreamSourceGenerator>,
69        card_index: usize,
70    ) -> Self {
71        StreamInfoBuilder {
72            stream_source_generator,
73            effects: vec![],
74            card_index,
75            #[cfg(windows)]
76            audio_client_guid: None,
77        }
78    }
79
80    /// Set the [`StreamEffect`]s to use when creating a stream from the stream source in
81    /// [`StreamInfo::prepare()`]. The default value is no effects.
82    pub fn effects(mut self, effects: Vec<StreamEffect>) -> Self {
83        self.effects = effects;
84        self
85    }
86
87    #[cfg(windows)]
88    pub fn audio_client_guid(mut self, audio_client_guid: Option<String>) -> Self {
89        self.audio_client_guid = audio_client_guid;
90        self
91    }
92
93    /// Builds a [`StreamInfo`].
94    pub fn build(self) -> StreamInfo {
95        self.into()
96    }
97}
98
99/// StreamInfo represents a virtio snd stream.
100///
101/// To create a StreamInfo, see [`StreamInfo::builder()`] and [`StreamInfoBuilder::build()`].
102pub struct StreamInfo {
103    pub(crate) stream_source: Option<SysAudioStreamSource>,
104    stream_source_generator: Arc<SysAudioStreamSourceGenerator>,
105    pub(crate) muted: Rc<AtomicBool>,
106    pub(crate) channels: u8,
107    pub(crate) format: SampleFormat,
108    pub(crate) frame_rate: u32,
109    buffer_bytes: usize,
110    pub(crate) period_bytes: usize,
111    direction: u8,  // VIRTIO_SND_D_*
112    pub state: u32, // VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)
113    // Stream effects to use when creating a new stream on [`prepare()`].
114    pub(crate) effects: Vec<StreamEffect>,
115
116    // just_reset set to true after reset. Make invalid state transition return Ok. Set to false
117    // after a valid state transition to SET_PARAMS or PREPARE.
118    pub just_reset: bool,
119
120    // Worker related
121    pub status_mutex: Rc<AsyncRwLock<WorkerStatus>>,
122    pub sender: Option<mpsc::UnboundedSender<DescriptorChain>>,
123    worker_future: Option<Box<dyn Future<Output = Result<(), Error>> + Unpin>>,
124    release_signal: Option<Rc<(AsyncRwLock<bool>, Condvar)>>, // Signal worker on release
125    card_index: usize,
126    ex: Option<Executor>, // Executor provided on `prepare()`. Used on `drop()`.
127    #[cfg(windows)]
128    pub(crate) playback_stream_cache: Option<(
129        Arc<AsyncRwLock<Box<dyn audio_streams::AsyncPlaybackBufferStream>>>,
130        Rc<AsyncRwLock<Box<dyn PlaybackBufferWriter>>>,
131    )>,
132    #[cfg(windows)]
133    pub(crate) audio_client_guid: Option<String>,
134}
135
136#[derive(Clone, Serialize, Deserialize)]
137pub struct StreamInfoSnapshot {
138    pub(crate) channels: u8,
139    pub(crate) format: SampleFormat,
140    pub(crate) frame_rate: u32,
141    buffer_bytes: usize,
142    pub(crate) period_bytes: usize,
143    direction: u8,  // VIRTIO_SND_D_*
144    pub state: u32, // VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)
145    effects: Vec<StreamEffect>,
146    pub just_reset: bool,
147}
148
149impl fmt::Debug for StreamInfo {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        f.debug_struct("StreamInfo")
152            .field("muted", &self.muted.load(Ordering::Relaxed))
153            .field("channels", &self.channels)
154            .field("format", &self.format)
155            .field("frame_rate", &self.frame_rate)
156            .field("buffer_bytes", &self.buffer_bytes)
157            .field("period_bytes", &self.period_bytes)
158            .field("direction", &get_virtio_direction_name(self.direction))
159            .field("state", &get_virtio_snd_r_pcm_cmd_name(self.state))
160            .field("effects", &self.effects)
161            .finish()
162    }
163}
164
165impl Drop for StreamInfo {
166    fn drop(&mut self) {
167        if let Some(ex) = self.ex.take() {
168            if self.state == VIRTIO_SND_R_PCM_START {
169                match ex.run_until(self.stop()) {
170                    Err(e) => error!("Drop stream error on stop in executor: {}", e),
171                    Ok(Err(e)) => error!("Drop stream error on stop: {}", e),
172                    _ => {}
173                }
174            }
175            if self.state == VIRTIO_SND_R_PCM_PREPARE || self.state == VIRTIO_SND_R_PCM_STOP {
176                match ex.run_until(self.release()) {
177                    Err(e) => error!("Drop stream error on release in executor: {}", e),
178                    Ok(Err(e)) => error!("Drop stream error on release: {}", e),
179                    _ => {}
180                }
181            }
182        }
183    }
184}
185
186impl From<StreamInfoBuilder> for StreamInfo {
187    fn from(builder: StreamInfoBuilder) -> Self {
188        StreamInfo {
189            muted: Rc::new(AtomicBool::new(false)),
190            stream_source: None,
191            stream_source_generator: builder.stream_source_generator,
192            channels: 0,
193            format: SampleFormat::U8,
194            frame_rate: 0,
195            buffer_bytes: 0,
196            period_bytes: 0,
197            direction: 0,
198            state: 0,
199            effects: builder.effects,
200            just_reset: false,
201            status_mutex: Rc::new(AsyncRwLock::new(WorkerStatus::Pause)),
202            sender: None,
203            worker_future: None,
204            release_signal: None,
205            card_index: builder.card_index,
206            ex: None,
207            #[cfg(windows)]
208            playback_stream_cache: None,
209            #[cfg(windows)]
210            audio_client_guid: builder.audio_client_guid,
211        }
212    }
213}
214
215impl StreamInfo {
216    /// Creates a minimal [`StreamInfoBuilder`]. See [`StreamInfoBuilder::new()`] for
217    /// the description of each parameter.
218    pub fn builder(
219        stream_source_generator: Arc<SysAudioStreamSourceGenerator>,
220        card_index: usize,
221    ) -> StreamInfoBuilder {
222        StreamInfoBuilder::new(stream_source_generator, card_index)
223    }
224
225    /// Sets parameters of the stream, putting it into [`VIRTIO_SND_R_PCM_SET_PARAMS`] state.
226    ///
227    /// * `params`: [`SetParams`] for the pcm stream runtime configuration.
228    pub async fn set_params(&mut self, params: SetParams) -> Result<(), Error> {
229        if self.state != 0
230            && self.state != VIRTIO_SND_R_PCM_SET_PARAMS
231            && self.state != VIRTIO_SND_R_PCM_PREPARE
232            && self.state != VIRTIO_SND_R_PCM_RELEASE
233        {
234            error!(
235                "[Card {}] Invalid PCM state transition from {} to {}",
236                self.card_index,
237                get_virtio_snd_r_pcm_cmd_name(self.state),
238                get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_SET_PARAMS)
239            );
240            return Err(Error::OperationNotSupported);
241        }
242
243        // Only required for PREPARE -> SET_PARAMS
244        self.release_worker().await;
245
246        self.channels = params.channels;
247        self.format = params.format;
248        self.frame_rate = params.frame_rate;
249        self.buffer_bytes = params.buffer_bytes;
250        self.period_bytes = params.period_bytes;
251        self.direction = params.dir;
252        self.state = VIRTIO_SND_R_PCM_SET_PARAMS;
253        self.just_reset = false;
254        Ok(())
255    }
256
257    /// Prepares the stream, putting it into [`VIRTIO_SND_R_PCM_PREPARE`] state.
258    ///
259    /// * `ex`: [`Executor`] to run the pcm worker.
260    /// * `tx_send`: Sender for sending `PcmResponse` for tx queue. (playback stream)
261    /// * `rx_send`: Sender for sending `PcmResponse` for rx queue. (capture stream)
262    pub async fn prepare(
263        &mut self,
264        ex: &Executor,
265        tx_send: &mpsc::UnboundedSender<PcmResponse>,
266        rx_send: &mpsc::UnboundedSender<PcmResponse>,
267    ) -> Result<(), Error> {
268        if self.state == 0 && self.just_reset {
269            return Ok(());
270        }
271        if self.state != VIRTIO_SND_R_PCM_SET_PARAMS
272            && self.state != VIRTIO_SND_R_PCM_PREPARE
273            && self.state != VIRTIO_SND_R_PCM_RELEASE
274        {
275            error!(
276                "[Card {}] Invalid PCM state transition from {} to {}",
277                self.card_index,
278                get_virtio_snd_r_pcm_cmd_name(self.state),
279                get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_PREPARE)
280            );
281            return Err(Error::OperationNotSupported);
282        }
283        self.just_reset = false;
284        if self.state == VIRTIO_SND_R_PCM_PREPARE {
285            self.release_worker().await;
286        }
287        let frame_size = self.channels as usize * self.format.sample_bytes();
288        if self.period_bytes % frame_size != 0 {
289            error!(
290                "[Card {}] period_bytes must be divisible by frame size",
291                self.card_index
292            );
293            return Err(Error::OperationNotSupported);
294        }
295        self.stream_source = Some(
296            self.stream_source_generator
297                .generate()
298                .map_err(Error::GenerateStreamSource)?,
299        );
300        let stream_objects = match self.direction {
301            VIRTIO_SND_D_OUTPUT => SysAsyncStreamObjects {
302                stream: self
303                    .create_directionstream_output(
304                        frame_size,
305                        #[cfg(windows)]
306                        self.audio_client_guid.clone(),
307                        ex,
308                    )
309                    .await?,
310                pcm_sender: tx_send.clone(),
311            },
312            VIRTIO_SND_D_INPUT => {
313                let buffer_reader = self.set_up_async_capture_stream(frame_size, ex).await?;
314                SysAsyncStreamObjects {
315                    stream: DirectionalStream::Input(self.period_bytes, Box::new(buffer_reader)),
316                    pcm_sender: rx_send.clone(),
317                }
318            }
319            _ => unreachable!(),
320        };
321
322        let (sender, receiver) = mpsc::unbounded();
323        self.sender = Some(sender);
324        self.state = VIRTIO_SND_R_PCM_PREPARE;
325
326        self.status_mutex = Rc::new(AsyncRwLock::new(WorkerStatus::Pause));
327        let period_dur = Duration::from_secs_f64(
328            self.period_bytes as f64 / frame_size as f64 / self.frame_rate as f64,
329        );
330        let release_signal = Rc::new((AsyncRwLock::new(false), Condvar::new()));
331        self.release_signal = Some(release_signal.clone());
332        let f = start_pcm_worker(
333            ex.clone(),
334            stream_objects.stream,
335            receiver,
336            self.status_mutex.clone(),
337            stream_objects.pcm_sender,
338            period_dur,
339            self.card_index,
340            self.muted.clone(),
341            release_signal,
342        );
343        self.worker_future = Some(Box::new(ex.spawn_local(f).into_future()));
344        self.ex = Some(ex.clone());
345        Ok(())
346    }
347
348    /// Starts the stream, putting it into [`VIRTIO_SND_R_PCM_START`] state.
349    pub async fn start(&mut self) -> Result<(), Error> {
350        if self.just_reset {
351            return Ok(());
352        }
353        if self.state != VIRTIO_SND_R_PCM_PREPARE && self.state != VIRTIO_SND_R_PCM_STOP {
354            error!(
355                "[Card {}] Invalid PCM state transition from {} to {}",
356                self.card_index,
357                get_virtio_snd_r_pcm_cmd_name(self.state),
358                get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_START)
359            );
360            return Err(Error::OperationNotSupported);
361        }
362        self.state = VIRTIO_SND_R_PCM_START;
363        let mut status = self.status_mutex.lock().await;
364        if *status != WorkerStatus::Quit {
365            *status = WorkerStatus::Running;
366        }
367        Ok(())
368    }
369
370    /// Stops the stream, putting it into [`VIRTIO_SND_R_PCM_STOP`] state.
371    pub async fn stop(&mut self) -> Result<(), Error> {
372        if self.just_reset {
373            return Ok(());
374        }
375        if self.state != VIRTIO_SND_R_PCM_START {
376            error!(
377                "[Card {}] Invalid PCM state transition from {} to {}",
378                self.card_index,
379                get_virtio_snd_r_pcm_cmd_name(self.state),
380                get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_STOP)
381            );
382            return Err(Error::OperationNotSupported);
383        }
384        self.state = VIRTIO_SND_R_PCM_STOP;
385        let mut status = self.status_mutex.lock().await;
386        if *status != WorkerStatus::Quit {
387            *status = WorkerStatus::Pause;
388        }
389        Ok(())
390    }
391
392    /// Releases the stream, putting it into [`VIRTIO_SND_R_PCM_RELEASE`] state.
393    pub async fn release(&mut self) -> Result<(), Error> {
394        if self.just_reset {
395            return Ok(());
396        }
397        if self.state != VIRTIO_SND_R_PCM_PREPARE && self.state != VIRTIO_SND_R_PCM_STOP {
398            error!(
399                "[Card {}] Invalid PCM state transition from {} to {}",
400                self.card_index,
401                get_virtio_snd_r_pcm_cmd_name(self.state),
402                get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_RELEASE)
403            );
404            return Err(Error::OperationNotSupported);
405        }
406        self.state = VIRTIO_SND_R_PCM_RELEASE;
407        self.stream_source = None;
408        self.release_worker().await;
409        Ok(())
410    }
411
412    async fn release_worker(&mut self) {
413        *self.status_mutex.lock().await = WorkerStatus::Quit;
414        if let Some(s) = self.sender.take() {
415            s.close_channel();
416        }
417
418        if let Some(release_signal) = self.release_signal.take() {
419            let (lock, cvar) = &*release_signal;
420            let mut signalled = lock.lock().await;
421            *signalled = true;
422            cvar.notify_all();
423        }
424
425        if let Some(f) = self.worker_future.take() {
426            f.await
427                .map_err(|error| {
428                    warn!(
429                        "[Card {}] Failure on releasing the worker_future: {}",
430                        self.card_index, error
431                    )
432                })
433                .ok();
434        }
435        self.ex.take(); // Remove ex as the worker is finished
436    }
437
438    pub fn snapshot(&self) -> StreamInfoSnapshot {
439        StreamInfoSnapshot {
440            channels: self.channels,
441            format: self.format,
442            frame_rate: self.frame_rate,
443            buffer_bytes: self.buffer_bytes,
444            period_bytes: self.period_bytes,
445            direction: self.direction, // VIRTIO_SND_D_*
446            // VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)
447            state: self.state,
448            effects: self.effects.clone(),
449            just_reset: self.just_reset,
450        }
451    }
452
453    pub fn restore(&mut self, state: &StreamInfoSnapshot) {
454        self.channels = state.channels;
455        self.format = state.format;
456        self.frame_rate = state.frame_rate;
457        self.buffer_bytes = state.buffer_bytes;
458        self.period_bytes = state.period_bytes;
459        self.direction = state.direction;
460        self.effects.clone_from(&state.effects);
461        self.just_reset = state.just_reset;
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use audio_streams::NoopStreamSourceGenerator;
468
469    use super::*;
470
471    fn new_stream() -> StreamInfo {
472        let card_index = 0;
473        StreamInfo::builder(
474            Arc::new(Box::new(NoopStreamSourceGenerator::new())),
475            card_index,
476        )
477        .build()
478    }
479
480    fn stream_set_params(
481        mut stream: StreamInfo,
482        ex: &Executor,
483        expected_ok: bool,
484        expected_state: u32,
485    ) -> StreamInfo {
486        let result = ex.run_until(stream.set_params(SetParams {
487            channels: 2,
488            format: SampleFormat::U8,
489            frame_rate: 48000,
490            buffer_bytes: 1024,
491            period_bytes: 512,
492            dir: VIRTIO_SND_D_OUTPUT,
493        }));
494        assert_eq!(result.unwrap().is_ok(), expected_ok);
495        assert_eq!(stream.state, expected_state);
496        stream
497    }
498
499    fn stream_prepare(
500        mut stream: StreamInfo,
501        ex: &Executor,
502        expected_ok: bool,
503        expected_state: u32,
504    ) -> StreamInfo {
505        let (tx_send, _) = mpsc::unbounded();
506        let (rx_send, _) = mpsc::unbounded();
507
508        let result = ex.run_until(stream.prepare(ex, &tx_send, &rx_send));
509        assert_eq!(result.unwrap().is_ok(), expected_ok);
510        assert_eq!(stream.state, expected_state);
511        stream
512    }
513
514    fn stream_start(
515        mut stream: StreamInfo,
516        ex: &Executor,
517        expected_ok: bool,
518        expected_state: u32,
519    ) -> StreamInfo {
520        let result = ex.run_until(stream.start());
521        assert_eq!(result.unwrap().is_ok(), expected_ok);
522        assert_eq!(stream.state, expected_state);
523        stream
524    }
525
526    fn stream_stop(
527        mut stream: StreamInfo,
528        ex: &Executor,
529        expected_ok: bool,
530        expected_state: u32,
531    ) -> StreamInfo {
532        let result = ex.run_until(stream.stop());
533        assert_eq!(result.unwrap().is_ok(), expected_ok);
534        assert_eq!(stream.state, expected_state);
535        stream
536    }
537
538    fn stream_release(
539        mut stream: StreamInfo,
540        ex: &Executor,
541        expected_ok: bool,
542        expected_state: u32,
543    ) -> StreamInfo {
544        let result = ex.run_until(stream.release());
545        assert_eq!(result.unwrap().is_ok(), expected_ok);
546        assert_eq!(stream.state, expected_state);
547        stream
548    }
549
550    #[test]
551    fn test_transitions_from_0() {
552        let ex = Executor::new().expect("Failed to create an executor");
553
554        // Valid transition to: {SET_PARAMS}
555        stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
556
557        // Invalid transition to: {PREPARE, START, STOP, RELEASE}
558        stream_prepare(new_stream(), &ex, false, 0);
559        stream_start(new_stream(), &ex, false, 0);
560        stream_stop(new_stream(), &ex, false, 0);
561        stream_release(new_stream(), &ex, false, 0);
562    }
563
564    #[test]
565    fn test_transitions_from_set_params() {
566        let ex = Executor::new().expect("Failed to create an executor");
567        let new_stream_set_params = || -> StreamInfo {
568            stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS)
569        };
570
571        // Valid transition to: {SET_PARAMS, PREPARE}
572        stream_set_params(
573            new_stream_set_params(),
574            &ex,
575            true,
576            VIRTIO_SND_R_PCM_SET_PARAMS,
577        );
578        stream_prepare(new_stream_set_params(), &ex, true, VIRTIO_SND_R_PCM_PREPARE);
579
580        // Invalid transition to: {START, STOP, RELEASE}
581        stream_start(
582            new_stream_set_params(),
583            &ex,
584            false,
585            VIRTIO_SND_R_PCM_SET_PARAMS,
586        );
587        stream_stop(
588            new_stream_set_params(),
589            &ex,
590            false,
591            VIRTIO_SND_R_PCM_SET_PARAMS,
592        );
593        stream_release(
594            new_stream_set_params(),
595            &ex,
596            false,
597            VIRTIO_SND_R_PCM_SET_PARAMS,
598        );
599    }
600
601    #[test]
602    fn test_transitions_from_prepare() {
603        let ex = Executor::new().expect("Failed to create an executor");
604        let new_stream_prepare = || -> StreamInfo {
605            let stream = stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
606            stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE)
607        };
608
609        // Valid transition to: {SET_PARAMS, PREPARE, START, RELEASE}
610        stream_set_params(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
611        stream_prepare(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_PREPARE);
612        stream_start(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_START);
613        stream_release(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);
614
615        // Invalid transition to: {STOP}
616        stream_stop(new_stream_prepare(), &ex, false, VIRTIO_SND_R_PCM_PREPARE);
617    }
618
619    #[test]
620    fn test_transitions_from_start() {
621        let ex = Executor::new().expect("Failed to create an executor");
622        let new_stream_start = || -> StreamInfo {
623            let mut stream =
624                stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
625            stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);
626            stream_start(stream, &ex, true, VIRTIO_SND_R_PCM_START)
627        };
628
629        // Valid transition to: {STOP}
630        stream_stop(new_stream_start(), &ex, true, VIRTIO_SND_R_PCM_STOP);
631
632        // Invalid transition to: {SET_PARAMS, PREPARE, START, RELEASE}
633        stream_set_params(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);
634        stream_prepare(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);
635        stream_start(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);
636        stream_release(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);
637    }
638
639    #[test]
640    fn test_transitions_from_stop() {
641        let ex = Executor::new().expect("Failed to create an executor");
642        let new_stream_stop = || -> StreamInfo {
643            let mut stream =
644                stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
645            stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);
646            stream = stream_start(stream, &ex, true, VIRTIO_SND_R_PCM_START);
647            stream_stop(stream, &ex, true, VIRTIO_SND_R_PCM_STOP)
648        };
649
650        // Valid transition to: {START, RELEASE}
651        stream_start(new_stream_stop(), &ex, true, VIRTIO_SND_R_PCM_START);
652        stream_release(new_stream_stop(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);
653
654        // Invalid transition to: {SET_PARAMS, PREPARE, STOP}
655        stream_set_params(new_stream_stop(), &ex, false, VIRTIO_SND_R_PCM_STOP);
656        stream_prepare(new_stream_stop(), &ex, false, VIRTIO_SND_R_PCM_STOP);
657        stream_stop(new_stream_stop(), &ex, false, VIRTIO_SND_R_PCM_STOP);
658    }
659
660    #[test]
661    fn test_transitions_from_release() {
662        let ex = Executor::new().expect("Failed to create an executor");
663        let new_stream_release = || -> StreamInfo {
664            let mut stream =
665                stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
666            stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);
667            stream_release(stream, &ex, true, VIRTIO_SND_R_PCM_RELEASE)
668        };
669
670        // Valid transition to: {SET_PARAMS, PREPARE}
671        stream_set_params(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
672        stream_prepare(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_PREPARE);
673
674        // Invalid transition to: {START, STOP, RELEASE}
675        stream_start(new_stream_release(), &ex, false, VIRTIO_SND_R_PCM_RELEASE);
676        stream_stop(new_stream_release(), &ex, false, VIRTIO_SND_R_PCM_RELEASE);
677        stream_release(new_stream_release(), &ex, false, VIRTIO_SND_R_PCM_RELEASE);
678    }
679
680    #[test]
681    fn test_transitions_from_0_just_reset() {
682        let ex = Executor::new().expect("Failed to create an executor");
683        let new_stream_0 = || -> StreamInfo {
684            let mut stream = new_stream();
685            stream.just_reset = true;
686            stream
687        };
688
689        // Valid transition to: {SET_PARAMS}
690        // After valid transition, just_reset reset to false
691        let mut stream = new_stream_0();
692        stream = stream_set_params(stream, &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
693        assert_eq!(stream.just_reset, false);
694
695        // Invalid transition to: {PREPARE, START, STOP, RELEASE}
696        // Return Ok but state doesn't change
697        stream_prepare(new_stream_0(), &ex, true, 0);
698        stream_start(new_stream_0(), &ex, true, 0);
699        stream_stop(new_stream_0(), &ex, true, 0);
700        stream_release(new_stream_0(), &ex, true, 0);
701    }
702
703    #[test]
704    fn test_transitions_from_set_params_just_reset() {
705        let ex = Executor::new().expect("Failed to create an executor");
706        let new_stream_set_params = || -> StreamInfo {
707            let mut stream =
708                stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
709            stream.just_reset = true;
710            stream
711        };
712
713        // Valid transition to: {SET_PARAMS, PREPARE}
714        // After valid transition, just_reset reset to false
715        let mut stream = new_stream_set_params();
716        stream = stream_set_params(stream, &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
717        assert_eq!(stream.just_reset, false);
718
719        let mut stream = new_stream_set_params();
720        stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);
721        assert_eq!(stream.just_reset, false);
722
723        // Invalid transition to: {START, STOP, RELEASE}
724        // Return Ok but state doesn't change
725        stream_start(
726            new_stream_set_params(),
727            &ex,
728            true,
729            VIRTIO_SND_R_PCM_SET_PARAMS,
730        );
731        stream_stop(
732            new_stream_set_params(),
733            &ex,
734            true,
735            VIRTIO_SND_R_PCM_SET_PARAMS,
736        );
737        stream_release(
738            new_stream_set_params(),
739            &ex,
740            true,
741            VIRTIO_SND_R_PCM_SET_PARAMS,
742        );
743    }
744
745    #[test]
746    fn test_transitions_from_release_just_reset() {
747        let ex = Executor::new().expect("Failed to create an executor");
748        let new_stream_release = || -> StreamInfo {
749            let mut stream =
750                stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
751            stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);
752            stream = stream_release(stream, &ex, true, VIRTIO_SND_R_PCM_RELEASE);
753            stream.just_reset = true;
754            stream
755        };
756
757        // Valid transition to: {SET_PARAMS, PREPARE}
758        // After valid transition, just_reset reset to false
759        let mut stream = new_stream_release();
760        stream = stream_set_params(stream, &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);
761        assert_eq!(stream.just_reset, false);
762
763        let mut stream = new_stream_release();
764        stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);
765        assert_eq!(stream.just_reset, false);
766
767        // Invalid transition to: {START, STOP, RELEASE}
768        // Return Ok but state doesn't change
769        stream_start(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);
770        stream_stop(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);
771        stream_release(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);
772    }
773
774    #[test]
775    fn test_stream_info_builder() {
776        let card_index = 0;
777        let builder = StreamInfo::builder(
778            Arc::new(Box::new(NoopStreamSourceGenerator::new())),
779            card_index,
780        )
781        .effects(vec![StreamEffect::EchoCancellation]);
782
783        let stream = builder.build();
784        assert_eq!(stream.effects, vec![StreamEffect::EchoCancellation]);
785    }
786}