/* Small program for testing scheduling latencies. Copyright (C) 2002 Jussi Laako */ #include #include #include #include #include #include #include #include #include #include #include #include /* this is considered safe, loss should never happen (92.8 msecs) */ /*#define BUF_SIZE 8192*/ /* this should work in all systems without loss (23 msecs) */ /*#define BUF_SIZE 2048*/ /* this is heavier but shouldn't cause loss (5.8 msecs) */ /*#define BUF_SIZE 512*/ /* lowlatency systems are be able to do this without loss (2.9 msecs) */ #define BUF_SIZE 256 #define AUDIO_DEV "/dev/dsp" /*#define AUDIO_DEV "/dev/dsp9"*/ volatile int run = 1; volatile int eventcntr = 0; signed short shared_buf[BUF_SIZE]; pthread_mutex_t mtx; pthread_cond_t cond; void set_scheduling () { uid_t current_uid; struct sched_param scheduler_params; current_uid = getuid(); setuid(0); scheduler_params.sched_priority = sched_get_priority_min(SCHED_FIFO); if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &scheduler_params) != 0) perror("pthread_setschedparam()"); setuid(current_uid); } void *producer_thread (void *param) { int h = (int) param; signed short buf[BUF_SIZE]; set_scheduling(); while (run) { if (read(h, buf, sizeof(buf)) != (int) sizeof(buf)) perror("read()"); pthread_mutex_lock(&mtx); memcpy(shared_buf, buf, BUF_SIZE * sizeof(signed short)); eventcntr++; pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mtx); } return NULL; } void *consumer_thread (void *param) { int h = (int) param; int local_eventcntr; signed short buf[BUF_SIZE]; set_scheduling(); pthread_mutex_lock(&mtx); local_eventcntr = eventcntr; pthread_mutex_unlock(&mtx); while (run) { pthread_mutex_lock(&mtx); pthread_cond_wait(&cond, &mtx); memcpy(buf, shared_buf, BUF_SIZE * sizeof(signed short)); local_eventcntr++; if (local_eventcntr != eventcntr) { printf("thread %u lost %i blocks\n", pthread_self(), eventcntr - local_eventcntr); local_eventcntr = eventcntr; } pthread_mutex_unlock(&mtx); if (write(h, buf, sizeof(buf)) != (int) sizeof(buf)) perror("write()"); } return NULL; } void sig_handler (int signo) { switch (signo) { case SIGINT: run = 0; break; } } int open_audio () { int devh; int fmt = AFMT_S16_NE; int chs = 2; int spd = 44100; int fragsize; int fragsetting; devh = open(AUDIO_DEV, O_RDONLY); if (devh < 0) { perror("open()"); return -1; } if (ioctl(devh, SNDCTL_DSP_SETFMT, &fmt) < 0) perror("ioctl(SNDCTL_DSP_SETFMT)"); if (ioctl(devh, SNDCTL_DSP_CHANNELS, &chs) < 0) perror("ioctl(SNDCTL_DSP_CHANNELS)"); if (ioctl(devh, SNDCTL_DSP_SPEED, &spd) < 0) perror("ioctl(SNDCTL_DSP_SPEED)"); fragsize = BUF_SIZE * sizeof(signed short); fragsetting = (0x7fff0000 | (fragsize & 0xffff)); if (ioctl(devh, SNDCTL_DSP_SETFRAGMENT, &fragsetting) < 0) perror("ioctl(SNDCTL_DSP_SETFRAGMENT)"); if (ioctl(devh, SNDCTL_DSP_GETBLKSIZE, &fragsize) < 0) perror("ioctl(SNDCTL_DSP_GETBLKSIZE)"); printf("fragment size: %i\n", fragsize); if (fragsize > (BUF_SIZE * sizeof(signed short))) puts("warning, hardware fragment size larger than requested"); return devh; } int open_null () { int devh; devh = open("/dev/null", O_WRONLY); if (devh < 0) perror("open()"); return devh; } int main (int argc, char *argv[]) { int audio_h; int null_h1; int null_h2; int recv_sig; sigset_t sigset_wait; pthread_t tid_producer; pthread_t tid_consumer1; pthread_t tid_consumer2; signal(SIGINT, sig_handler); sigemptyset(&sigset_wait); sigaddset(&sigset_wait, SIGINT); audio_h = open_audio(); if (audio_h < 0) return 1; null_h1 = open_null(); if (null_h1 < 0) return 1; null_h2 = open_null(); if (null_h2 < 0) return 1; pthread_mutex_init(&mtx, NULL); pthread_cond_init(&cond, NULL); pthread_create(&tid_producer, NULL, producer_thread, (void *) audio_h); pthread_create(&tid_consumer1, NULL, consumer_thread, (void *) null_h1); pthread_create(&tid_consumer2, NULL, consumer_thread, (void *) null_h2); sigwait(&sigset_wait, &recv_sig); run = 0; pthread_join(tid_consumer1, NULL); pthread_join(tid_consumer2, NULL); pthread_join(tid_producer, NULL); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mtx); close(audio_h); close(null_h1); close(null_h2); return 0; }