Skip to content

Commit 5710ab3

Browse files
committed
tools: Add a new test for pause/resume
The test runs the specified number of pause/resume iterations on a combination of a playback PCM and a capture PCM looping through all available playback and capture PCM devices available in the SOF audio card. It skips the deep buffer playback PCMs for the time being. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
1 parent d10020d commit 5710ab3

1 file changed

Lines changed: 386 additions & 0 deletions

File tree

tools/pause_resume_test.c

Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
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

Comments
 (0)