/* * simple multi-thread stress test for PCM * * The main thread simply feeds or reads the sample data with the * random size continuously. Meanwhile, the worker threads call some * update function depending on the given mode, and show the thread * number of the read value. * * The function for the worker thread is specified via -m option. * When the random mode ('r') is set, the update function is chosen * randomly in the loop. * * When the -v option is passed, this tries to show some obtained value * from the function. Without -v, as default, it shows the thread number * (0-9). In addition, it puts the mode suffix ('a' for avail, 'd' for * delay, etc) for the random mode, as well as the suffix '!' indicating * the error from the called function. */ #include #include #include #include "../include/asoundlib.h" #define MAX_THREADS 10 enum { MODE_AVAIL_UPDATE, MODE_STATUS, MODE_HWSYNC, MODE_TIMESTAMP, MODE_DELAY, MODE_RANDOM }; static char mode_suffix[] = { 'a', 's', 'h', 't', 'd', 'r' }; static const char *devname = "default"; static int stream = SND_PCM_STREAM_PLAYBACK; static int num_threads = 1; static int periodsize = 16 * 1024; static int bufsize = 16 * 1024 * 4; static int channels = 2; static int rate = 48000; static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; static int running_mode = MODE_AVAIL_UPDATE; static int show_value = 0; static int quiet = 0; static pthread_t peeper_threads[MAX_THREADS]; static int running = 1; static snd_pcm_t *pcm; static void *peeper(void *data) { int thread_no = (long)data; snd_pcm_sframes_t val; snd_pcm_status_t *stat; snd_htimestamp_t tstamp; int mode = running_mode, err; snd_pcm_status_alloca(&stat); while (running) { if (running_mode == MODE_RANDOM) mode = rand() % MODE_RANDOM; switch (mode) { case MODE_AVAIL_UPDATE: val = snd_pcm_avail_update(pcm); err = 0; break; case MODE_STATUS: err = snd_pcm_status(pcm, stat); val = snd_pcm_status_get_avail(stat); break; case MODE_HWSYNC: err = snd_pcm_hwsync(pcm); break; case MODE_TIMESTAMP: err = snd_pcm_htimestamp(pcm, (snd_pcm_uframes_t *)&val, &tstamp); break; default: err = snd_pcm_delay(pcm, &val); break; } if (quiet) continue; if (running_mode == MODE_RANDOM) { fprintf(stderr, "%d%c%s", thread_no, mode_suffix[mode], err ? "!" : ""); } else { if (show_value && mode != MODE_HWSYNC) fprintf(stderr, "\r%d ", (int)val); else fprintf(stderr, "%d%s", thread_no, err ? "!" : ""); } } return NULL; } static void usage(void) { fprintf(stderr, "usage: multi-thread [-options]\n"); fprintf(stderr, " -D str Set device name\n"); fprintf(stderr, " -r val Set sample rate\n"); fprintf(stderr, " -p val Set period size (in frame)\n"); fprintf(stderr, " -b val Set buffer size (in frame)\n"); fprintf(stderr, " -c val Set number of channels\n"); fprintf(stderr, " -f str Set PCM format\n"); fprintf(stderr, " -s str Set stream direction (playback or capture)\n"); fprintf(stderr, " -t val Set number of threads\n"); fprintf(stderr, " -m str Running mode (avail, status, hwsync, timestamp, delay, random)\n"); fprintf(stderr, " -v Show value\n"); fprintf(stderr, " -q Quiet mode\n"); } static int parse_options(int argc, char **argv) { int c, i; while ((c = getopt(argc, argv, "D:r:f:p:b:s:t:m:vq")) >= 0) { switch (c) { case 'D': devname = optarg; break; case 'r': rate = atoi(optarg); break; case 'p': periodsize = atoi(optarg); break; case 'b': bufsize = atoi(optarg); break; case 'c': channels = atoi(optarg); break; case 'f': format = snd_pcm_format_value(optarg); break; case 's': if (*optarg == 'p' || *optarg == 'P') stream = SND_PCM_STREAM_PLAYBACK; else if (*optarg == 'c' || *optarg == 'C') stream = SND_PCM_STREAM_CAPTURE; else { fprintf(stderr, "invalid stream direction\n"); return 1; } break; case 't': num_threads = atoi(optarg); if (num_threads < 1 || num_threads > MAX_THREADS) { fprintf(stderr, "invalid number of threads\n"); return 1; } break; case 'm': for (i = 0; i <= MODE_RANDOM; i++) if (mode_suffix[i] == *optarg) break; if (i > MODE_RANDOM) { fprintf(stderr, "invalid mode type\n"); return 1; } running_mode = i; break; case 'v': show_value = 1; break; case 'q': quiet = 1; break; default: usage(); return 1; } } return 0; } static int setup_params(void) { snd_pcm_hw_params_t *hw; /* FIXME: more finer error checks */ snd_pcm_hw_params_alloca(&hw); snd_pcm_hw_params_any(pcm, hw); snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format(pcm, hw, format); snd_pcm_hw_params_set_channels(pcm, hw, channels); snd_pcm_hw_params_set_rate(pcm, hw, rate, 0); snd_pcm_hw_params_set_period_size(pcm, hw, periodsize, 0); snd_pcm_hw_params_set_buffer_size(pcm, hw, bufsize); if (snd_pcm_hw_params(pcm, hw) < 0) { fprintf(stderr, "snd_pcm_hw_params error\n"); return 1; } return 0; } int main(int argc, char **argv) { char *buf; int i, err; if (parse_options(argc, argv)) return 1; err = snd_pcm_open(&pcm, devname, stream, 0); if (err < 0) { fprintf(stderr, "cannot open pcm %s\n", devname); return 1; } if (setup_params()) return 1; buf = calloc(1, snd_pcm_format_size(format, bufsize) * channels); if (!buf) { fprintf(stderr, "cannot alloc buffer\n"); return 1; } for (i = 0; i < num_threads; i++) { if (pthread_create(&peeper_threads[i], NULL, peeper, (void *)(long)i)) { fprintf(stderr, "pthread_create error\n"); return 1; } } if (stream == SND_PCM_STREAM_CAPTURE) snd_pcm_start(pcm); for (;;) { int size = rand() % (bufsize / 2); if (stream == SND_PCM_STREAM_PLAYBACK) err = snd_pcm_writei(pcm, buf, size); else err = snd_pcm_readi(pcm, buf, size); if (err < 0) { fprintf(stderr, "read/write error %d\n", err); err = snd_pcm_recover(pcm, err, 0); if (err < 0) break; if (stream == SND_PCM_STREAM_CAPTURE) snd_pcm_start(pcm); } } running = 0; for (i = 0; i < num_threads; i++) pthread_cancel(peeper_threads[i]); for (i = 0; i < num_threads; i++) pthread_join(peeper_threads[i], NULL); return 1; }