1+ #include <stdio.h>
2+ #include <stdlib.h>
3+ #include <alsa/asoundlib.h>
4+ #include <unistd.h>
5+ #include <pthread.h>
6+ #include <stdbool.h>
7+
8+ // This program requires ALSA library and a Linux environment with ALSA support.
9+ // sudo apt install libasound2-dev
10+ // To compile: gcc -o pcm_pause pause_test.c -lasound
11+ // To run: ./pcm_pause [pause_count] or ./pcm_pause [pause_count] [playback_device_id] [capture_device_id]
12+ // When the playback_device_id and capture_device_id are provided, it will run the pause/resume test only for that combination.
13+ // Otherwise, the test runs the test for all combinations of playback and capture devices found on the system.
14+ // Deep buffer devices are skipped at the moment.
15+
16+ #define BUFFER_SIZE 4096
17+ #define MAX_PCMS_PLAYBACK 10
18+ #define MAX_PCMS_CAPTURE 10
19+ #define MAX_DEVICE_NAME_LENGTH 32
20+
21+ snd_pcm_t * playback_handle , * capture_handle ;
22+ char playback_devices [MAX_PCMS_PLAYBACK ][MAX_DEVICE_NAME_LENGTH ];
23+ char capture_devices [MAX_PCMS_CAPTURE ][MAX_DEVICE_NAME_LENGTH ];
24+ char buffer [BUFFER_SIZE ] = {0 }; // Buffer to simulate playback
25+ char capture_buffer [BUFFER_SIZE ] = {0 }; // Buffer to store captured data
26+ bool stop_threads = false; // Flag to signal threads to stop
27+ int pause_count = 20 ;
28+ int num_playback_devices = 0 ;
29+ int num_capture_devices = 0 ;
30+ int playback_framesize = 0 ;
31+ int capture_framesize = 0 ;
32+
33+ void list_pcm_devices () {
34+ int card = -1 ; // Start with the first card
35+ snd_ctl_t * ctl ;
36+ snd_ctl_card_info_t * card_info ;
37+ snd_pcm_info_t * pcm_info ;
38+
39+ snd_ctl_card_info_malloc (& card_info );
40+ snd_pcm_info_malloc (& pcm_info );
41+
42+ while (snd_card_next (& card ) >= 0 && card >= 0 ) {
43+ char card_name [32 ];
44+ snprintf (card_name , sizeof (card_name ), "hw:%d" , card );
45+
46+ if (snd_ctl_open (& ctl , card_name , 0 ) < 0 ) {
47+ fprintf (stderr , "Cannot open control for card %d\n" , card );
48+ continue ;
49+ }
50+
51+ if (snd_ctl_card_info (ctl , card_info ) < 0 ) {
52+ fprintf (stderr , "Cannot get card info for card %d\n" , card );
53+ snd_ctl_close (ctl );
54+ continue ;
55+ }
56+
57+ // if card name doesnt contain "sof", skip it
58+ if (strstr (snd_ctl_card_info_get_name (card_info ), "sof" ) == NULL ) {
59+ snd_ctl_close (ctl );
60+ continue ;
61+ }
62+
63+ fprintf (stdout , "Card %d: %s\n" , card , snd_ctl_card_info_get_name (card_info ));
64+
65+ int device = -1 ;
66+ while (snd_ctl_pcm_next_device (ctl , & device ) >= 0 && device >= 0 ) {
67+ snd_pcm_info_set_device (pcm_info , device );
68+ snd_pcm_info_set_subdevice (pcm_info , 0 );
69+ snd_pcm_info_set_stream (pcm_info , SND_PCM_STREAM_PLAYBACK );
70+
71+ // add to playback devices list
72+ if (snd_ctl_pcm_info (ctl , pcm_info ) >= 0 && num_playback_devices < MAX_PCMS_PLAYBACK ) {
73+ // Skip deep buffer device
74+ if (device != 31 )
75+ snprintf (playback_devices [num_playback_devices ++ ], MAX_DEVICE_NAME_LENGTH , "hw:%d,%d" , card , device );
76+ }
77+
78+ snd_pcm_info_set_stream (pcm_info , SND_PCM_STREAM_CAPTURE );
79+ // add to capture devices list
80+ if (snd_ctl_pcm_info (ctl , pcm_info ) >= 0 && num_capture_devices < MAX_PCMS_CAPTURE )
81+ snprintf (capture_devices [num_capture_devices ++ ], MAX_DEVICE_NAME_LENGTH , "hw:%d,%d" , card , device );
82+ }
83+ }
84+
85+ snd_ctl_close (ctl );
86+
87+ snd_ctl_card_info_free (card_info );
88+ snd_pcm_info_free (pcm_info );
89+
90+ // print all playback devices
91+ fprintf (stdout , "Available Playback Devices:\n" );
92+ for (int i = 0 ; i < num_playback_devices ; i ++ )
93+ fprintf (stdout , " %s\n" , playback_devices [i ]);
94+
95+ // print all capture devices
96+ fprintf (stdout , "Available Capture Devices:\n" );
97+ for (int i = 0 ; i < num_capture_devices ; i ++ )
98+ fprintf (stdout , " %s\n" , capture_devices [i ]);
99+ }
100+
101+ void cleanup_pcm_handles () {
102+ if (playback_handle ) {
103+ snd_pcm_close (playback_handle );
104+ playback_handle = NULL ;
105+ }
106+ if (capture_handle ) {
107+ snd_pcm_close (capture_handle );
108+ capture_handle = NULL ;
109+ }
110+ }
111+
112+ void * playback_thread (void * arg ) {
113+ while (!stop_threads ) {
114+ int err = snd_pcm_writei (playback_handle , buffer , playback_framesize );
115+ if (err < 0 ) {
116+ fprintf (stderr , "Error writing to playback device: %s\n" , snd_strerror (err ));
117+ break ;
118+ }
119+ // calculate wait time based on the period size and sample rate
120+
121+ usleep ((playback_framesize / 48000 ) * 1000000 ); // Convert to microseconds
122+ }
123+ return NULL ;
124+ }
125+
126+ void * capture_thread (void * arg ) {
127+ while (!stop_threads ) {
128+ int err = snd_pcm_readi (capture_handle , capture_buffer , capture_framesize );
129+ if (err < 0 ) {
130+ fprintf (stderr , "Error reading from capture device: %s\n" , snd_strerror (err ));
131+ cleanup_pcm_handles ();
132+ break ;
133+ }
134+ usleep ((capture_framesize / 48000 ) * 1000000 ); // Convert to microseconds
135+ }
136+ return NULL ;
137+ }
138+
139+ int run_pause_resume_test (char * playback_device , char * capture_device ) {
140+ snd_pcm_hw_params_t * playback_params_1 , * capture_params ;
141+ int err , i ;
142+
143+ // Open first playback PCM device
144+ err = snd_pcm_open (& playback_handle , playback_device , SND_PCM_STREAM_PLAYBACK , 0 );
145+ if (err < 0 ) {
146+ fprintf (stderr , "Error opening playback device %s: %s\n" , playback_device , snd_strerror (err ));
147+ return 1 ;
148+ }
149+ printf ("Playback device %s opened successfully.\n" , playback_device );
150+
151+ // Open capture PCM device
152+ err = snd_pcm_open (& capture_handle , capture_device , SND_PCM_STREAM_CAPTURE , 0 );
153+ if (err < 0 ) {
154+ fprintf (stderr , "Error opening capture device %s: %s\n" , capture_device , snd_strerror (err ));
155+ snd_pcm_close (playback_handle );
156+ return 1 ;
157+ }
158+ printf ("Capture device %s opened successfully.\n" , capture_device );
159+
160+ // Configure first playback PCM device
161+ snd_pcm_hw_params_malloc (& playback_params_1 );
162+ snd_pcm_hw_params_any (playback_handle , playback_params_1 );
163+ snd_pcm_hw_params_set_access (playback_handle , playback_params_1 , SND_PCM_ACCESS_RW_INTERLEAVED );
164+ //TODO: check if either 16-bit/2ch is supported for playback
165+ snd_pcm_hw_params_set_format (playback_handle , playback_params_1 , SND_PCM_FORMAT_S16_LE );
166+ snd_pcm_hw_params_set_channels (playback_handle , playback_params_1 , 2 );
167+ snd_pcm_hw_params_set_rate (playback_handle , playback_params_1 , 48000 , 0 );
168+ playback_framesize = BUFFER_SIZE / (2 * 4 ); // 2 channels, 4 bytes per sample
169+
170+ // Set period size for playback
171+ err = snd_pcm_hw_params_set_period_size (playback_handle , playback_params_1 , playback_framesize , 0 );
172+ if (err < 0 ) {
173+ fprintf (stderr , "Error setting period size for playback device %s: %s\n" , playback_device , snd_strerror (err ));
174+ snd_pcm_hw_params_free (playback_params_1 );
175+ snd_pcm_close (playback_handle );
176+ snd_pcm_close (capture_handle );
177+ return 1 ;
178+ }
179+ snd_pcm_hw_params (playback_handle , playback_params_1 );
180+ snd_pcm_hw_params_free (playback_params_1 );
181+
182+ // Configure capture PCM device
183+ snd_pcm_hw_params_malloc (& capture_params );
184+ snd_pcm_hw_params_any (capture_handle , capture_params );
185+ snd_pcm_hw_params_set_access (capture_handle , capture_params , SND_PCM_ACCESS_RW_INTERLEAVED );
186+
187+ int capture_channels = 2 ;
188+ int capture_sample_bytes = 2 ;
189+ /* check if either 16-bit or 32-bit format is supported for capture */
190+ if (snd_pcm_hw_params_test_format (capture_handle , capture_params , SND_PCM_FORMAT_S16_LE ) == 0 ) {
191+ snd_pcm_hw_params_set_format (capture_handle , capture_params , SND_PCM_FORMAT_S16_LE );
192+ }else if (snd_pcm_hw_params_test_format (capture_handle , capture_params , SND_PCM_FORMAT_S32_LE ) == 0 ) {
193+ snd_pcm_hw_params_set_format (capture_handle , capture_params , SND_PCM_FORMAT_S32_LE );
194+ capture_sample_bytes = 4 ; // 32-bit format
195+ } else {
196+ fprintf (stderr , "Unsupported format for capture device %s\n" , capture_device );
197+ snd_pcm_close (playback_handle );
198+ snd_pcm_close (capture_handle );
199+ return 1 ;
200+ }
201+
202+ /* check if either 2 or 4 channels are supported for capture */
203+ if (snd_pcm_hw_params_test_channels (capture_handle , capture_params , 2 ) == 0 ) {
204+ snd_pcm_hw_params_set_channels (capture_handle , capture_params , 2 );
205+ } else if (snd_pcm_hw_params_test_channels (capture_handle , capture_params , 4 ) == 0 ) {
206+ snd_pcm_hw_params_set_channels (capture_handle , capture_params , 4 );
207+ capture_channels = 4 ;
208+ } else {
209+ fprintf (stderr , "Unsupported number of channels for capture device %s\n" ,
210+ capture_device );
211+ snd_pcm_close (playback_handle );
212+ snd_pcm_close (capture_handle );
213+ return 1 ;
214+ }
215+ capture_framesize = BUFFER_SIZE / (capture_channels * capture_sample_bytes );
216+
217+ snd_pcm_hw_params_set_rate (capture_handle , capture_params , 48000 , 0 );
218+
219+ // Set period size for capture
220+ err = snd_pcm_hw_params_set_period_size (capture_handle , capture_params ,
221+ capture_framesize , 0 );
222+ if (err < 0 ) {
223+ fprintf (stderr , "Error setting period size for capture device %s: %s\n" ,
224+ capture_device , snd_strerror (err ));
225+ snd_pcm_close (playback_handle );
226+ snd_pcm_close (capture_handle );
227+ snd_pcm_hw_params_free (capture_params );
228+ return 1 ;
229+ }
230+ snd_pcm_hw_params (capture_handle , capture_params );
231+ snd_pcm_hw_params_free (capture_params );
232+
233+ // Prepare the PCM playback stream
234+ err = snd_pcm_prepare (playback_handle );
235+ if (err < 0 ) {
236+ fprintf (stderr , "Error preparing playback device %s: %s\n" , playback_device , snd_strerror (err ));
237+ snd_pcm_close (playback_handle );
238+ return 1 ;
239+ }
240+
241+ // Prepare the PCM capture stream
242+ err = snd_pcm_prepare (capture_handle );
243+ if (err < 0 ) {
244+ fprintf (stderr , "Error preparing playback device %s: %s\n" , playback_device , snd_strerror (err ));
245+ snd_pcm_close (playback_handle );
246+ return 1 ;
247+ }
248+
249+ printf ("Playback and capture devices configured successfully.\n" );
250+
251+ // Create threads for playback and capture
252+ pthread_t playback_tid , capture_tid ;
253+ pthread_create (& playback_tid , NULL , playback_thread , NULL );
254+ pthread_create (& capture_tid , NULL , capture_thread , NULL );
255+
256+ // Perform pause/resume in the main loop
257+ for (i = 0 ; i < pause_count ; i ++ ) {
258+ snd_pcm_state_t state = snd_pcm_state (playback_handle );
259+
260+ while (state != SND_PCM_STATE_RUNNING ) {
261+ usleep (10000 ); // Wait until playback is running
262+ state = snd_pcm_state (playback_handle );
263+ }
264+ // Pause playback device
265+ err = snd_pcm_pause (playback_handle , 1 );
266+ if (err < 0 ) {
267+ fprintf (stderr , "Error pausing playback device %s at iteration %d: %s\n" , playback_device , i , snd_strerror (err ));
268+ break ;
269+ }
270+ ("Playback device %s paused at iteration %d.\n" , playback_device , i );
271+
272+ // Pause capture device
273+ err = snd_pcm_pause (capture_handle , 1 );
274+ if (err < 0 ) {
275+ fprintf (stderr , "Error pausing capture device %s at iteration %d: %s\n" , capture_device , i , snd_strerror (err ));
276+ break ;
277+ }
278+ printf ("Capture device %s paused at iteration %d.\n" , capture_device , i );
279+
280+ usleep (10000 ); // Simulate a short pause
281+
282+ // Resume playback device
283+ err = snd_pcm_pause (playback_handle , 0 );
284+ if (err < 0 ) {
285+ fprintf (stderr , "Error resuming playback device %s at iteration %d: %s\n" , playback_device , i , snd_strerror (err ));
286+ break ;
287+ }
288+ printf ("Playback device %s resumed at iteration %d.\n" , playback_device , i );
289+
290+ // Resume capture device
291+ err = snd_pcm_pause (capture_handle , 0 );
292+ if (err < 0 ) {
293+ fprintf (stderr , "Error resuming capture device %s at iteration %d: %s\n" , capture_device , i , snd_strerror (err ));
294+ break ;
295+ }
296+ printf ("Capture device %s resumed at iteration %d.\n" , capture_device , i );
297+ }
298+
299+ // Signal playback and capture threads to stop
300+ stop_threads = true;
301+
302+ // Wait for playback and capture threads to finish
303+ pthread_join (playback_tid , NULL );
304+ pthread_join (capture_tid , NULL );
305+
306+ // Close PCM devices
307+ cleanup_pcm_handles ();
308+
309+ printf ("PCM devices closed.\n" );
310+ if (i < pause_count - 1 )
311+ return 1 ; // Return error if any pause/resume failed
312+ return 0 ; // Return success if all operations were successful
313+
314+ }
315+
316+ int main (int argc , char * argv []) {
317+ // Initialize ALSA
318+ snd_lib_error_set_handler (NULL );
319+
320+ // Check for user input for pause_count
321+ if (argc > 1 ) {
322+ pause_count = atoi (argv [1 ]); // Convert command-line argument to integer
323+ if (pause_count <= 0 ) {
324+ fprintf (stderr , "Invalid pause_count value. It must be a positive integer.\n" );
325+ return 1 ;
326+ }
327+ }
328+
329+ // check for user input for playback and capture devices and run pause/resume only for the specified combination
330+ if (argc > 3 ) {
331+ int playback_device_id = atoi (argv [2 ]); // Convert playback device id
332+ int capture_device_id = atoi (argv [3 ]); // Convert capture device id
333+ char capture_device [MAX_DEVICE_NAME_LENGTH ];
334+ char playback_device [MAX_DEVICE_NAME_LENGTH ];
335+
336+ snprintf (playback_device , MAX_DEVICE_NAME_LENGTH , "hw:0,%d" , playback_device_id );
337+ snprintf (capture_device , MAX_DEVICE_NAME_LENGTH , "hw:0,%d" , capture_device_id );
338+ // run pause/resume test
339+ if (run_pause_resume_test (playback_device , capture_device ) != 0 ) {
340+ fprintf (stderr , "Pause/Resume test failed for playback device: %s and capture device: %s\n" ,
341+ playback_device , capture_device );
342+ return 1 ;
343+ }
344+ printf ("Pause/Resume test completed successfully for playback device: %s and capture device: %s\n" ,
345+ playback_device , capture_device );
346+
347+ return 0 ;
348+ }
349+
350+ // List available PCM devices
351+ list_pcm_devices ();
352+
353+ // run pause/resume test with 1 playback and 1 capture device
354+ if (num_playback_devices == 0 || num_capture_devices == 0 ) {
355+ fprintf (stderr , "No playback or capture devices found.\n" );
356+ return 1 ;
357+ }
358+
359+ // loop through all capture devices for each playback device
360+ for (int i = 0 ; i < num_playback_devices ; i ++ ) {
361+ for (int j = 0 ; j < num_capture_devices ; j ++ ) {
362+ printf ("Running pause/resume test for playback device: %s and capture device: %s\n" ,
363+ playback_devices [i ], capture_devices [j ]);
364+
365+ // reset stop_threads flag
366+ stop_threads = false;
367+
368+ // run pause/resume test
369+ if (run_pause_resume_test (playback_devices [i ], capture_devices [j ]) != 0 ) {
370+ fprintf (stderr , "Pause/Resume test failed for playback device: %s and capture device: %s\n" ,
371+ playback_devices [i ], capture_devices [j ]);
372+ return 1 ;
373+ }
374+ // Wait before next test
375+ printf ("Pause/Resume test completed for playback device: %s and capture device: %s\n" ,
376+ playback_devices [i ], capture_devices [j ]);
377+ printf ("Waiting before next test...\n" );
378+ // Sleep for 3 seconds before next test
379+ usleep (3000000 );
380+
381+ }
382+ }
383+
384+ printf ("Pause/Resume test completed successfully.\n" );
385+ return 0 ;
386+ }
0 commit comments