1 use super::utils::{test_get_default_device, test_ops_stream_operation, Scope};
2 use super::*;
3 use std::sync::atomic::{AtomicI64, Ordering};
4 
5 #[test]
test_dial_tone()6 fn test_dial_tone() {
7     use std::f32::consts::PI;
8     use std::thread;
9     use std::time::Duration;
10 
11     const SAMPLE_FREQUENCY: u32 = 48_000;
12 
13     // Do nothing if there is no available output device.
14     if test_get_default_device(Scope::Output).is_none() {
15         println!("No output device.");
16         return;
17     }
18 
19     // Make sure the parameters meet the requirements of AudioUnitContext::stream_init
20     // (in the comments).
21     let mut output_params = ffi::cubeb_stream_params::default();
22     output_params.format = ffi::CUBEB_SAMPLE_S16NE;
23     output_params.rate = SAMPLE_FREQUENCY;
24     output_params.channels = 1;
25     output_params.layout = ffi::CUBEB_LAYOUT_MONO;
26     output_params.prefs = ffi::CUBEB_STREAM_PREF_NONE;
27 
28     struct Closure {
29         buffer_size: AtomicI64,
30         phase: i64,
31     };
32     let mut closure = Closure {
33         buffer_size: AtomicI64::new(0),
34         phase: 0,
35     };
36     let closure_ptr = &mut closure as *mut Closure as *mut c_void;
37 
38     test_ops_stream_operation(
39         "stream: North American dial tone",
40         ptr::null_mut(), // Use default input device.
41         ptr::null_mut(), // No input parameters.
42         ptr::null_mut(), // Use default output device.
43         &mut output_params,
44         4096, // TODO: Get latency by get_min_latency instead ?
45         Some(data_callback),
46         Some(state_callback),
47         closure_ptr,
48         |stream| {
49             assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
50 
51             #[derive(Debug)]
52             enum State {
53                 WaitingForStart,
54                 PositionIncreasing,
55                 Paused,
56                 Resumed,
57                 End,
58             };
59             let mut state = State::WaitingForStart;
60             let mut position: u64 = 0;
61             let mut prev_position: u64 = 0;
62             let mut count = 0;
63             const CHECK_COUNT: i32 = 10;
64             loop {
65                 thread::sleep(Duration::from_millis(50));
66                 assert_eq!(
67                     unsafe { OPS.stream_get_position.unwrap()(stream, &mut position) },
68                     ffi::CUBEB_OK
69                 );
70                 println!(
71                     "State: {:?}, position: {}, previous position: {}",
72                     state, position, prev_position
73                 );
74                 match &mut state {
75                     State::WaitingForStart => {
76                         // It's expected to have 0 for a few iterations here: the stream can take
77                         // some time to start.
78                         if position != prev_position {
79                             assert!(position > prev_position);
80                             prev_position = position;
81                             state = State::PositionIncreasing;
82                         }
83                     }
84                     State::PositionIncreasing => {
85                         // wait a few iterations, check monotony
86                         if position != prev_position {
87                             assert!(position > prev_position);
88                             prev_position = position;
89                             count += 1;
90                             if count > CHECK_COUNT {
91                                 state = State::Paused;
92                                 count = 0;
93                                 assert_eq!(
94                                     unsafe { OPS.stream_stop.unwrap()(stream) },
95                                     ffi::CUBEB_OK
96                                 );
97                                 // Update the position once paused.
98                                 assert_eq!(
99                                     unsafe {
100                                         OPS.stream_get_position.unwrap()(stream, &mut position)
101                                     },
102                                     ffi::CUBEB_OK
103                                 );
104                                 prev_position = position;
105                             }
106                         }
107                     }
108                     State::Paused => {
109                         // The cubeb_stream_stop call above should synchrously stop the callbacks,
110                         // hence the clock, the assert below must always holds, modulo the client
111                         // side interpolation.
112                         assert!(
113                             position == prev_position
114                                 || position - prev_position
115                                     <= closure.buffer_size.load(Ordering::SeqCst) as u64
116                         );
117                         count += 1;
118                         prev_position = position;
119                         if count > CHECK_COUNT {
120                             state = State::Resumed;
121                             count = 0;
122                             assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
123                         }
124                     }
125                     State::Resumed => {
126                         // wait a few iterations, this can take some time to start
127                         if position != prev_position {
128                             assert!(position > prev_position);
129                             prev_position = position;
130                             count += 1;
131                             if count > CHECK_COUNT {
132                                 state = State::End;
133                                 count = 0;
134                                 assert_eq!(
135                                     unsafe { OPS.stream_stop.unwrap()(stream) },
136                                     ffi::CUBEB_OK
137                                 );
138                                 assert_eq!(
139                                     unsafe {
140                                         OPS.stream_get_position.unwrap()(stream, &mut position)
141                                     },
142                                     ffi::CUBEB_OK
143                                 );
144                                 prev_position = position;
145                             }
146                         }
147                     }
148                     State::End => {
149                         // The cubeb_stream_stop call above should synchrously stop the callbacks,
150                         // hence the clock, the assert below must always holds, modulo the client
151                         // side interpolation.
152                         assert!(
153                             position == prev_position
154                                 || position - prev_position
155                                     <= closure.buffer_size.load(Ordering::SeqCst) as u64
156                         );
157                         if position == prev_position {
158                             count += 1;
159                             if count > CHECK_COUNT {
160                                 break;
161                             }
162                         }
163                     }
164                 }
165             }
166             assert_eq!(unsafe { OPS.stream_stop.unwrap()(stream) }, ffi::CUBEB_OK);
167         },
168     );
169 
170     extern "C" fn state_callback(
171         stream: *mut ffi::cubeb_stream,
172         user_ptr: *mut c_void,
173         state: ffi::cubeb_state,
174     ) {
175         assert!(!stream.is_null());
176         assert!(!user_ptr.is_null());
177         assert_ne!(state, ffi::CUBEB_STATE_ERROR);
178     }
179 
180     extern "C" fn data_callback(
181         stream: *mut ffi::cubeb_stream,
182         user_ptr: *mut c_void,
183         _input_buffer: *const c_void,
184         output_buffer: *mut c_void,
185         nframes: i64,
186     ) -> i64 {
187         assert!(!stream.is_null());
188         assert!(!user_ptr.is_null());
189         assert!(!output_buffer.is_null());
190 
191         let buffer = unsafe {
192             let ptr = output_buffer as *mut i16;
193             let len = nframes as usize;
194             slice::from_raw_parts_mut(ptr, len)
195         };
196 
197         let closure = unsafe { &mut *(user_ptr as *mut Closure) };
198 
199         closure.buffer_size.store(nframes, Ordering::SeqCst);
200 
201         // Generate tone on the fly.
202         for data in buffer.iter_mut() {
203             let t1 = (2.0 * PI * 350.0 * (closure.phase) as f32 / SAMPLE_FREQUENCY as f32).sin();
204             let t2 = (2.0 * PI * 440.0 * (closure.phase) as f32 / SAMPLE_FREQUENCY as f32).sin();
205             *data = f32_to_i16_sample(0.5 * (t1 + t2));
206             closure.phase += 1;
207         }
208 
209         nframes
210     }
211 
212     fn f32_to_i16_sample(x: f32) -> i16 {
213         (x * f32::from(i16::max_value())) as i16
214     }
215 }
216